fix(docs): every published quickstart was dead on arrival

Two independent defects made every documented 'first API call' fail:

1. FICTIONAL MODEL IDS. `zen-omni` and `zen-coder` appear ZERO times in the
   live catalog (api.hanzo.ai/v1/models, 440 models). Every SDK sample -
   python, typescript, go, cpp, swift, kotlin - told the customer to call a
   model that does not exist, so even a correct request 400s. Corrected to the
   real ids: zen5 and zen5-coder.

2. DEAD HOST + STALE PINNED MODEL. llm.hanzo.ai is retired and 301s (a POST
   returns the literal string 'Permanent Redirect', not a completion), and
   `claude-sonnet-4-5-20250929` is long superseded. Both removed: the host is
   gone from customer-facing docs entirely rather than documented as retired,
   and pinned upstream ids give way to our own current zen5.

50 files. Verified against the live catalog, not assumed.
This commit is contained in:
z
2026-07-25 16:11:04 -07:00
parent 6bd7099ecc
commit a9306750ac
79 changed files with 802 additions and 1369 deletions
+1 -1
View File
@@ -119,7 +119,7 @@ jobs:
-p "{\"spec\":{\"image\":{\"tag\":\"${GITHUB_SHA}\"}}}"
./kubectl -n hanzo rollout status deploy/docs --timeout=300s
# Re-index search (search.hanzo.ai) + the zen-coder-flash doc chat so both
# Re-index search (search.hanzo.ai) + the zen5-coder-flash doc chat so both
# answer from the just-shipped content. Best-effort — never block a deploy.
- name: Re-index docs corpus
continue-on-error: true
+1 -1
View File
@@ -259,7 +259,7 @@ export default function PlatformPage() {
<div className="flex flex-col items-center gap-0.5 text-fd-muted-foreground/40">
<span className="text-[10px] font-mono">AI API</span>
<span className="text-xs">|</span>
<span className="text-[10px] font-mono">llm.hanzo.ai</span>
<span className="text-[10px] font-mono">api.hanzo.ai/v1</span>
</div>
</div>
</section>
@@ -1600,7 +1600,7 @@ Example:
agents: {
defaults: {
models: {
"anthropic/claude-sonnet-4-5-20250929": {
"anthropic/zen5": {
params: { temperature: 0.6 }
},
"openai/gpt-5.2": {
+1 -1
View File
@@ -184,7 +184,7 @@ export default function Page() {
<span className="text-blue-400">{'"model"'}</span>
{': '}
<span className="text-green-400">
{'"claude-sonnet-4-5-20250929"'}
{'"zen5"'}
</span>
{',\n'}
{' '}
+2 -2
View File
@@ -16,7 +16,7 @@ import { Agent, Runtime } from '@hanzo/agents'
const agent = new Agent({
name: 'researcher',
model: 'claude-sonnet-4-5-20250929',
model: 'zen5',
brain: { org: 'startx' }, // shared memory
tools: ['search', 'fetch', 'brain.recall'],
})
@@ -30,7 +30,7 @@ from hanzo_agents import Agent, Runtime
agent = Agent(
name="researcher",
model="claude-sonnet-4-5-20250929",
model="zen5",
brain={"org": "startx"},
tools=["search", "fetch", "brain.recall"],
)
+37 -45
View File
@@ -57,20 +57,7 @@ Widget keys are designed for client-side use. They have restricted scopes -- typ
- **Rate limit** -- requests per minute cap
7. Copy the key immediately -- it will not be shown again
### Via the API
```bash
curl -X POST https://llm.hanzo.ai/key/generate \
-H "Authorization: Bearer hk-your-admin-key" \
-H "Content-Type: application/json" \
-d '{
"key_alias": "ci-pipeline",
"max_budget": 100.0,
"budget_duration": "monthly",
"tpm_limit": 100000,
"rpm_limit": 600
}'
```
Key issuance, budgets, and revocation are console operations — there is no public key-minting endpoint, by design: a credential that can mint credentials is the one thing you never want reachable over HTTP with a bearer token.
## Using Keys
@@ -80,14 +67,17 @@ All Hanzo APIs accept keys via the `Authorization` header with the `Bearer` sche
Authorization: Bearer hk-proj-abc123...
```
This works uniformly across every endpoint:
Every service answers on the one host, `api.hanzo.ai`, under `/v1/<service>` — the same key works across all of them:
| Service | Base URL | Example |
|---------|----------|---------|
| API Gateway | `api.hanzo.ai` | `api.hanzo.ai/v1/models` |
| AI API | `llm.hanzo.ai` | `llm.hanzo.ai/v1/chat/completions` |
| Service | Path | Example |
|---------|------|---------|
| AI | `/v1/chat/completions`, `/v1/models`, `/v1/embeddings` | `api.hanzo.ai/v1/chat/completions` |
| Search | `/v1/search` | `api.hanzo.ai/v1/search` |
| Secrets | `/v1/kms` | `api.hanzo.ai/v1/kms/orgs/{org}/secrets` |
| Deploys | `/v1/platform` | `api.hanzo.ai/v1/platform/projects` |
| Analytics | `/v1/analytics` | `api.hanzo.ai/v1/analytics/overview` |
| Observability | `/v1/o11y` | `api.hanzo.ai/v1/o11y/status?product=cloud` |
| Tasks | `tasks-api.hanzo.ai` | gRPC with metadata header |
| Search | `search.hanzo.ai` | `search.hanzo.ai/v1/search` |
### SDK Configuration
@@ -96,13 +86,13 @@ All official SDKs accept the API key as a constructor parameter:
```python
# Python
from openai import OpenAI
client = OpenAI(api_key="hk-your-key", base_url="https://llm.hanzo.ai/v1")
client = OpenAI(api_key="hk-your-key", base_url="https://api.hanzo.ai/v1")
```
```typescript
// TypeScript
import OpenAI from 'openai'
const client = new OpenAI({ apiKey: 'hk-your-key', baseURL: 'https://llm.hanzo.ai/v1' })
const client = new OpenAI({ apiKey: 'hk-your-key', baseURL: 'https://api.hanzo.ai/v1' })
```
```go
@@ -123,42 +113,53 @@ These keys operate across all projects within an organization. They are used for
## Key Information and Usage
Check a key's budget and usage:
Spend and usage for the calling credential come from the billing and analytics APIs — same host, same key:
```bash
curl https://llm.hanzo.ai/key/info \
# Current balance
curl https://api.hanzo.ai/v1/billing/balance \
-H "Authorization: Bearer hk-your-key"
# Itemized metered usage
curl https://api.hanzo.ai/v1/billing/usage \
-H "Authorization: Bearer hk-your-key"
```
Response:
```json
{ "balance": 0, "holds": 0, "available": 0 }
```
```json
{
"key_alias": "prod-backend",
"max_budget": 500.0,
"spend": 127.43,
"budget_duration": "monthly",
"tpm_limit": 500000,
"rpm_limit": 3000,
"expires": "2026-12-31T00:00:00Z"
"count": 2000,
"usage": [
{
"amount": 24,
"createdAt": "2026-07-25T21:59:35Z",
"metadata": { "model": "zen-image" },
"transactionId": "use_daff3c10f561635114c7a78e8d0eea5d"
}
]
}
```
For rolled-up request/token/spend counts across a range, use `GET /v1/analytics/overview?range=30d` — see [Analytics](/docs/services/analytics).
## Rotation Best Practices
1. **Rotate regularly** -- Set key expiration at creation time. 90 days is a reasonable default for production keys.
2. **Use KMS for production** -- Store keys in [Hanzo KMS](https://kms.hanzo.ai) and sync them to your runtime environment via KMSSecret CRDs. Never hardcode keys in source or commit them to git.
2. **Use KMS for production** -- Store keys in [Hanzo KMS](/docs/services/kms) and sync them to your runtime environment via KMSSecret CRDs. Never hardcode keys in source or commit them to git.
3. **Create before you revoke** -- Generate the replacement key, deploy it, verify it works, then revoke the old one. Zero-downtime rotation.
4. **One key per service** -- Give each service or environment its own key. This limits blast radius and makes audit logs useful.
5. **Set budgets** -- Every production key should have a monthly budget. This prevents runaway costs from bugs or abuse.
6. **Monitor usage** -- Check `key/info` or the console dashboard regularly. Unusual spend patterns may indicate a leak.
6. **Monitor usage** -- Check `/v1/billing/usage` or the console dashboard regularly. Unusual spend patterns may indicate a leak.
### KMS Integration
For production deployments, use Hanzo KMS to manage API keys as secrets:
```yaml
# KMSSecret CRD -- syncs secret from kms.hanzo.ai to K8s
# KMSSecret CRD -- syncs a secret from Hanzo KMS into K8s
apiVersion: secrets.hanzo.ai/v1alpha1
kind: KMSSecret
metadata:
@@ -175,16 +176,7 @@ spec:
## Revoking Keys
Revoke a key immediately from the console or via the API:
```bash
curl -X POST https://llm.hanzo.ai/key/delete \
-H "Authorization: Bearer hk-your-admin-key" \
-H "Content-Type: application/json" \
-d '{"keys": ["hk-proj-abc123..."]}'
```
Revocation is immediate. Any in-flight request using the key will fail with `401 Unauthorized`.
Revoke a key from the console: **Project → API Keys → Revoke**. Revocation is immediate — any in-flight request using the key fails with `401 Unauthorized`.
## Security Rules
+22 -18
View File
@@ -7,15 +7,15 @@ description: The one way to authenticate against Hanzo IAM — canonical OIDC en
Hanzo uses one identity provider per brand, a standards-compliant OIDC provider. You integrate through exactly one library — [`@hanzo/iam`](https://www.npmjs.com/package/@hanzo/iam) — against one set of endpoints. There is no second way.
| Brand | IAM origin (`serverUrl`) | Login UI |
|-------|--------------------------|----------|
| Hanzo | `https://iam.hanzo.ai` | `hanzo.id` |
| Lux | `https://lux.id` | `lux.id` |
| Zoo | `https://zoo.id` | `zoo.id` |
| Bootnode | `https://id.bootno.de` | `id.bootno.de` |
| Pars | `https://pars.id` | `pars.id` |
| Brand | IAM origin (`serverUrl`) = issuer + login UI |
|-------|----------------------------------------------|
| Hanzo | `https://hanzo.id` |
| Lux | `https://lux.id` |
| Zoo | `https://zoo.id` |
| Bootnode | `https://id.bootno.de` |
| Pars | `https://pars.id` |
The SDK is brand-agnostic. Select the brand with `serverUrl`; nothing else changes.
One origin per brand — it is the OIDC `issuer`, the login UI, and the endpoint base, all the same host. The SDK is brand-agnostic: select the brand with `serverUrl`; nothing else changes.
## OIDC Endpoints
@@ -23,12 +23,16 @@ These `/v1/iam/oauth/*` paths are the only endpoints. There is no `/oauth/*`, no
| Purpose | Path |
|---------|------|
| Discovery | `https://iam.hanzo.ai/.well-known/openid-configuration` |
| Authorize | `https://iam.hanzo.ai/v1/iam/oauth/authorize` |
| Token | `https://iam.hanzo.ai/v1/iam/oauth/token` |
| UserInfo | `https://iam.hanzo.ai/v1/iam/oauth/userinfo` |
| JWKS | `https://iam.hanzo.ai/v1/iam/.well-known/jwks` |
| Logout | `https://iam.hanzo.ai/v1/iam/oauth/logout` |
| Discovery | `https://hanzo.id/.well-known/openid-configuration` |
| Authorize | `https://hanzo.id/v1/iam/oauth/authorize` |
| Token | `https://hanzo.id/v1/iam/oauth/token` |
| UserInfo | `https://hanzo.id/v1/iam/oauth/userinfo` |
| JWKS | `https://hanzo.id/v1/iam/.well-known/jwks` |
| Logout | `https://hanzo.id/v1/iam/oauth/logout` |
<Callout title="Why these live on hanzo.id, not api.hanzo.ai">
Every Hanzo **API** call goes to `api.hanzo.ai/v1/<service>` — one host, one key. OIDC is the one exception, and not by choice: the endpoint URLs must match the `issuer` the server publishes, because that is what clients validate the `iss` claim against. `curl https://hanzo.id/.well-known/openid-configuration` returns `"issuer": "https://hanzo.id"`, so `hanzo.id` is the answer. The IAM **management** API (`get-account`, `get-users`, …) is a normal API and lives at `api.hanzo.ai/v1/iam/*` like everything else.
</Callout>
Every flow uses **PKCE `S256`**, **`client_secret_basic`** for confidential clients, and scopes **`openid profile email`**. The flow is Authorization Code + PKCE; implicit grant is not supported.
@@ -60,7 +64,7 @@ Any backend validates a bearer token like this:
import { validateToken } from '@hanzo/iam/server'
const result = await validateToken(accessToken, {
serverUrl: process.env.IAM_ENDPOINT!, // https://iam.hanzo.ai
serverUrl: process.env.IAM_ENDPOINT!, // https://hanzo.id
clientId: process.env.IAM_CLIENT_ID!,
})
@@ -135,7 +139,7 @@ Register the redirect URI `https://<app-host>/api/auth/callback/iam`.
import { IAM } from '@hanzo/iam/browser'
const iam = new IAM({
serverUrl: 'https://iam.hanzo.ai',
serverUrl: 'https://hanzo.id',
clientId: 'hanzo-myspa',
redirectUri: `${location.origin}/auth/callback`,
})
@@ -152,7 +156,7 @@ import { IamProvider, useIam } from '@hanzo/iam/react'
function Root() {
return (
<IamProvider serverUrl="https://iam.hanzo.ai" clientId="hanzo-myspa">
<IamProvider serverUrl="https://hanzo.id" clientId="hanzo-myspa">
<App />
</IamProvider>
)
@@ -173,7 +177,7 @@ import passport from 'passport'
import { createIamPassportStrategy } from '@hanzo/iam/passport'
passport.use('iam', createIamPassportStrategy({
serverUrl: 'https://iam.hanzo.ai',
serverUrl: 'https://hanzo.id',
clientId: 'hanzo-myservice',
clientSecret: process.env.IAM_CLIENT_SECRET!,
callbackUrl: 'https://myservice.hanzo.ai/v1/sso/oidc/callback',
+4 -7
View File
@@ -30,14 +30,11 @@ curl https://api.hanzo.ai/v1/billing/balance \
Top up credits with a card through the billing portal, or on-chain with HUSD (see below).
### Trial vs. Prepaid Credits
### Credits
Your balance is made of two kinds of credit, and they spend in a fixed order:
Your balance is **prepaid credit** -- what you add yourself by topping up (card or HUSD), plus any program awards such as the [Startup Program](/docs/startups) or [Referrals](/docs/referrals).
- **Trial credits** -- every new account starts with **$5 in free trial credits**, no card required. They exist so you can build before you pay.
- **Prepaid credits** -- what you add yourself by topping up (card or HUSD), plus any program awards such as the [Startup Program](/docs/startups) or [Referrals](/docs/referrals).
**Trial credits always burn first.** Only once they are exhausted does metering start drawing down your prepaid balance. Both live in the one balance above, and every billable action -- an API call, a [deploy](/docs/deploy), a stored object, a compute-second -- draws from it. When your prepaid balance runs low you get a `402 Payment Required` until you top up.
It is one balance, and every billable action -- an API call, a [deploy](/docs/deploy), a stored object, a compute-second -- draws from it. When it runs low you get a `402 Payment Required` until you top up.
Watch it all in the console's **Usage** and **Billing** views, or at [billing.hanzo.ai](https://billing.hanzo.ai) -- both read the same live balance and metered usage as the API below.
@@ -108,7 +105,7 @@ Metering, pricing, credits, invoices, and payment methods are one connected syst
## Related
- [Getting Started](/docs/getting-started) -- claim your $5 free credits and make your first call
- [Getting Started](/docs/getting-started) -- create an account, get a key, make your first call
- [Startup Program](/docs/startups) -- up to $150,000 in credits for venture-backed startups
- [Plans & Pricing](/docs/services/platform/pricing/plans) -- subscription tiers and per-unit rates
- [Referrals & Affiliates](/docs/referrals) -- earn cloud credits by referring developers
@@ -57,7 +57,7 @@ endpoints:
apiKey: "${ANTHROPIC_API_KEY}"
models:
default:
- claude-sonnet-4-5-20250929
- zen5
- claude-opus-4-20250514
google:
+2 -2
View File
@@ -22,7 +22,7 @@ curl -X POST https://api.hanzo.ai/v1/evals/runs \
"model": "qwen3-4b",
"runName": "qwen3-baseline",
"judge": {
"model": "claude-sonnet-4-5-20250929",
"model": "zen5",
"criteria": "Is the answer correct and grounded in the expected output?"
}
}'
@@ -32,7 +32,7 @@ curl -X POST https://api.hanzo.ai/v1/evals/runs \
{
"dataset": "support-qa",
"model": "qwen3-4b",
"judgeModel": "claude-sonnet-4-5-20250929",
"judgeModel": "zen5",
"runName": "qwen3-baseline",
"items": 40,
"scored": 40,
+15 -13
View File
@@ -18,8 +18,8 @@ Go to [hanzo.id](https://hanzo.id) and sign up with:
Your Hanzo identity works everywhere: console, chat, platform, cloud, and all API services. One account, one login.
<Callout title="$5 free to start -- no card required">
Every new account gets **$5 in free trial credits**. That is enough to make your first API calls, run a chat, or try a deploy today -- no payment method needed. Trial credits burn first; you only add a card or crypto balance when you are ready to keep going. See [Credits & Billing](/docs/billing).
<Callout title="One account, one balance">
The same identity and the same prepaid balance cover every product -- AI calls, deploys, storage. Add a card or crypto balance when you are ready to make your first call. See [Credits & Billing](/docs/billing).
</Callout>
## 2. Open the Console
@@ -58,20 +58,22 @@ The `hk-` prefix identifies it as a Hanzo API key. See [API Keys](/docs/api-keys
## 5. Make Your First API Call
All Hanzo services are available through `api.hanzo.ai`. The AI API is at `llm.hanzo.ai`. Both accept the same `hk-*` API key.
Every Hanzo service — AI, storage, identity, deploys — answers on **one host, `api.hanzo.ai`**, under `/v1/<service>`, with the same `hk-*` key. There is no second API host to configure.
### curl
```bash
curl https://llm.hanzo.ai/v1/chat/completions \
curl https://api.hanzo.ai/v1/chat/completions \
-H "Authorization: Bearer hk-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5-20250929",
"model": "zen5",
"messages": [{"role": "user", "content": "Hello from Hanzo!"}]
}'
```
Browse every model id with `curl https://api.hanzo.ai/v1/models` — the Zen family plus 400+ third-party models, all on the same key.
### Python
Install the SDK:
@@ -89,11 +91,11 @@ from openai import OpenAI
client = OpenAI(
api_key="hk-your-api-key",
base_url="https://llm.hanzo.ai/v1",
base_url="https://api.hanzo.ai/v1",
)
response = client.chat.completions.create(
model="claude-sonnet-4-5-20250929",
model="zen5",
messages=[{"role": "user", "content": "Hello from Hanzo!"}],
)
print(response.choices[0].message.content)
@@ -116,26 +118,26 @@ import OpenAI from 'openai'
const client = new OpenAI({
apiKey: 'hk-your-api-key',
baseURL: 'https://llm.hanzo.ai/v1',
baseURL: 'https://api.hanzo.ai/v1',
})
const completion = await client.chat.completions.create({
model: 'claude-sonnet-4-5-20250929',
model: 'zen5',
messages: [{ role: 'user', content: 'Hello from Hanzo!' }],
})
console.log(completion.choices[0].message.content)
```
Prefer a UI? Open [chat.hanzo.ai](https://chat.hanzo.ai) and start chatting -- it spends from the same balance, so your $5 works there too.
Prefer a UI? Open [chat.hanzo.ai](https://chat.hanzo.ai) and start chatting -- it spends from the same balance.
## 6. Add Credits When You're Ready
## 6. Add Credits
Your $5 in trial credits burn down first as you make calls. When you want to keep going, top up a **prepaid balance** at [billing.hanzo.ai](https://billing.hanzo.ai):
Top up a **prepaid balance** at [billing.hanzo.ai](https://billing.hanzo.ai):
- **Card** -- pay by card through the billing portal (processed by Square).
- **Crypto** -- top up on-chain with **HUSD**, the platform stablecoin, from a connected wallet.
Trial credits always spend before your prepaid balance, and every product -- API calls, deploys, storage -- draws down the same balance. Watch usage in the console's [Usage & Billing](/docs/billing) views. For how metering, credits, and invoices fit together, see [Credits & Billing](/docs/billing).
Every product -- API calls, deploys, storage -- draws down the same balance. Watch usage in the console's [Usage & Billing](/docs/billing) views. For how metering, credits, and invoices fit together, see [Credits & Billing](/docs/billing).
Building a funded startup? You may qualify for **up to $150,000 in credits** through the [Startup Program](/docs/startups).
+13 -9
View File
@@ -13,15 +13,17 @@ import { Card, Cards } from '@hanzo/docs-base-ui/components/card';
One API for **200+ language models** from every major provider. OpenAI-compatible interface with load balancing, caching, rate limiting, fallbacks, and full observability.
```bash title="Quick Start"
curl https://llm.hanzo.ai/v1/chat/completions \
curl https://api.hanzo.ai/v1/chat/completions \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5-20250929",
"model": "zen5",
"messages": [{"role": "user", "content": "Hello"}]
}'
```
The AI API is on the same host as every other Hanzo service: `api.hanzo.ai`, one `hk-*` key, no second base URL. List model ids with `GET https://api.hanzo.ai/v1/models`.
## Why Hanzo AI API?
- **200+ Models** — Claude, GPT, Gemini, Llama, Mistral, and more
@@ -53,11 +55,11 @@ from openai import OpenAI
client = OpenAI(
api_key="your-hanzo-api-key",
base_url="https://llm.hanzo.ai/v1"
base_url="https://api.hanzo.ai/v1"
)
response = client.chat.completions.create(
model="claude-sonnet-4-5-20250929",
model="zen5",
messages=[{"role": "user", "content": "Explain quantum computing"}]
)
print(response.choices[0].message.content)
@@ -70,11 +72,11 @@ import OpenAI from 'openai'
const client = new OpenAI({
apiKey: process.env.HANZO_API_KEY,
baseURL: 'https://llm.hanzo.ai/v1',
baseURL: 'https://api.hanzo.ai/v1',
})
const completion = await client.chat.completions.create({
model: 'claude-sonnet-4-5-20250929',
model: 'zen5',
messages: [{ role: 'user', content: 'Explain quantum computing' }],
})
console.log(completion.choices[0].message.content)
@@ -97,7 +99,7 @@ console.log(completion.choices[0].message.content)
model_list:
- model_name: "default"
litellm_params:
model: "anthropic/claude-sonnet-4-5-20250929"
model: "anthropic/zen5"
api_key: "os.environ/ANTHROPIC_API_KEY"
- model_name: "default"
@@ -118,6 +120,8 @@ router_settings:
## API Endpoints
All relative to `https://api.hanzo.ai`.
| Endpoint | Description |
|----------|-------------|
| `POST /v1/chat/completions` | Chat completions (streaming supported) |
@@ -125,8 +129,8 @@ router_settings:
| `POST /v1/embeddings` | Text embeddings |
| `POST /v1/images/generations` | Image generation |
| `GET /v1/models` | List available models |
| `POST /key/generate` | Create API keys with budgets |
| `GET /key/info` | Key usage and budget info |
API keys, budgets, and per-key usage are managed in the [Console](https://console.hanzo.ai) — see [API Keys](/docs/api-keys).
## Related
+2 -2
View File
@@ -24,7 +24,7 @@ curl https://api.hanzo.ai/v1/pricing/models \
{
"models": [
{
"id": "claude-sonnet-4-5-20250929",
"id": "zen5",
"name": "Claude Sonnet 4.5",
"provider": "anthropic",
"context": 200000,
@@ -46,7 +46,7 @@ curl https://api.hanzo.ai/v1/chat/completions \
-H "Authorization: Bearer hk-..." \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5-20250929",
"model": "zen5",
"messages": [{"role": "user", "content": "Hi"}]
}'
```
@@ -541,7 +541,7 @@ ai:
- `model` - The model to use (default: provider-specific)
- OpenAI: `gpt-4o`, `gpt-4`, `gpt-3.5-turbo`, etc.
- Anthropic: `claude-3-5-sonnet-20241022`, `claude-3-opus-20240229`, etc.
- Anthropic: `claude-3-5-sonnet-20241022`, `zen5`, etc.
- OpenRouter: Use their model naming like `anthropic/claude-3.5-sonnet`
</details>
@@ -64,7 +64,7 @@ Add to VS Code settings.json:
// Anthropic
"hanzo.llm.anthropic.apiKey": "sk-ant-...",
"hanzo.llm.anthropic.model": "claude-3-opus-20240229"
"hanzo.llm.anthropic.model": "zen5"
}
```
@@ -31,7 +31,7 @@ the historical mess where `HANZO_IAM_URL` / `HANZO_CLIENT_ID` /
| Prefix | What it's for | Examples |
|---|---|---|
| `IAM_*` | The auth/OIDC engine (user identity, JWT issuance, session) | `IAM_URL`, `IAM_CLIENT_ID`, `IAM_CLIENT_SECRET`, `IAM_AUDIENCE`, `IAM_ORG`, `IAM_REDIRECT_URI` |
| `HANZO_*` | Hanzo **product** API access at `api.hanzo.ai` / `llm.hanzo.ai` / `chat.hanzo.ai` etc — your developer credentials to *use* Hanzo as a paid service | `HANZO_API_KEY`, `HANZO_OAUTH_CLIENT_ID`, `HANZO_OAUTH_CLIENT_SECRET`, `HANZO_OAUTH_REDIRECT_URI` |
| `HANZO_*` | Hanzo **product** API access at `api.hanzo.ai` (one host for every `/v1/<service>`), `chat.hanzo.ai` etc — your developer credentials to *use* Hanzo as a paid service | `HANZO_API_KEY`, `HANZO_OAUTH_CLIENT_ID`, `HANZO_OAUTH_CLIENT_SECRET`, `HANZO_OAUTH_REDIRECT_URI` |
| `IDV_*` | Identity-verification providers (biometric, KYC vendors) | `IDV_URL`, `IDV_CLIENT_ID`, `IDV_CLIENT_SECRET` |
| `CASDOOR_*` | **DEAD** — engine isn't Casdoor-branded anymore | (delete) |
@@ -81,7 +81,7 @@ where the value is consumed:
- Used by `@hanzo/iam` SDK, points at an IAM instance, validates JWKS,
hits `/v1/iam/oauth/*` → IAM_*
- Used by `@hanzo/api` (or raw fetch) against `https://api.hanzo.ai` /
`https://llm.hanzo.ai` for LLM/Chat/MCP → HANZO_*
`https://api.hanzo.ai/v1` for AI/Chat/MCP → HANZO_*
- Used by a biometric / KYC IDV provider → IDV_*
A single app can (and often does) carry env vars from all three
@@ -12,7 +12,7 @@ project rather than introduce a new sync.
| Attribute | Value |
|---|---|
| Hostname | `kms.hanzo.ai` |
| Hostname | `api.hanzo.ai` (one API host; KMS is the `/v1/kms` prefix) |
| Project slug | `hanzo-iam` |
| Environment slug | `prod` (mainnet), `test` (testnet), `dev` (devnet) |
| Path | `/` (existing IAM secrets are flat under root) |
@@ -47,20 +47,23 @@ export those values into your shell:
```bash
export KMS_CLIENT_ID=$(kubectl --context=do-sfo3-hanzo-k8s -n hanzo \
get secret iam-kms-auth -o jsonpath='{.data.CLIENTID}' | base64 -d)
get secret iam-kms-auth -o jsonpath='{.data.clientId}' | base64 -d)
export KMS_CLIENT_SECRET=$(kubectl --context=do-sfo3-hanzo-k8s -n hanzo \
get secret iam-kms-auth -o jsonpath='{.data.CLIENTSECRET}' | base64 -d)
get secret iam-kms-auth -o jsonpath='{.data.clientSecret}' | base64 -d)
```
Mint a short-lived token (KMS Universal Auth is an OIDC-bearer scheme):
Exchange the machine identity for a short-lived bearer (JSON body, not form-encoded):
```bash
TOKEN=$(curl -sS -X POST https://kms.hanzo.ai/v1/auth/universal/login \
-d "clientId=$KMS_CLIENT_ID&clientSecret=$KMS_CLIENT_SECRET" \
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)
test -n "$TOKEN" || { echo "auth failed"; exit 1; }
test -n "$TOKEN" -a "$TOKEN" != null || { echo "auth failed"; exit 1; }
```
Response: `{"accessToken":"<RS256 JWT>","expiresIn":604800,"tokenType":"Bearer"}`.
## Write the 12 secrets
Repeat once per env (mainnet→prod, testnet→test, devnet→dev). Substitute
@@ -82,24 +85,24 @@ declare -A SECRETS=(
[IAM_GOOGLE_CLIENT_SECRET]="<phase-1-${ENV}-google-client-secret>"
)
for key in "${!SECRETS[@]}"; do
curl -sS -X PATCH "https://kms.hanzo.ai/api/v3/secrets/raw/$key" \
curl -sS -X POST "https://api.hanzo.ai/v1/kms/orgs/hanzo/secrets" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg project hanzo-iam \
--arg path "/hanzo-iam" \
--arg name "$key" \
--arg env "$KMS_ENV" \
--arg path "/" \
--arg val "${SECRETS[$key]}" \
'{projectSlug:$project, environment:$env, secretPath:$path, secretValue:$val}')"
'{path:$path, name:$name, env:$env, value:$val}')"
echo
done
```
Verify by listing the path:
Verify by listing the namespace (metadata only — values are never listed):
```bash
curl -sS "https://kms.hanzo.ai/api/v3/secrets/list?projectSlug=hanzo-iam&environment=$KMS_ENV&secretPath=/" \
-H "Authorization: Bearer $TOKEN" | jq '.secrets[].secretKey' \
curl -sS "https://api.hanzo.ai/v1/kms/orgs/hanzo/secrets?env=$KMS_ENV" \
-H "Authorization: Bearer $TOKEN" | jq -r '.secrets[].name' \
| grep -E 'IAM_(GITHUB|GOOGLE)_CLIENT_(ID|SECRET)'
```
@@ -147,11 +150,13 @@ env:
## Rotation
```bash
# 1. PATCH the new value into KMS
curl -sS -X PATCH "https://kms.hanzo.ai/api/v3/secrets/raw/IAM_GITHUB_CLIENT_SECRET" \
# 1. PATCH the new value into KMS (If-Match carries the current version;
# a missing precondition is 428, a stale one is 409)
curl -sS -X PATCH "https://api.hanzo.ai/v1/kms/orgs/hanzo/secrets/hanzo-iam/IAM_GITHUB_CLIENT_SECRET?env=prod" \
-H "Authorization: Bearer $TOKEN" \
-H "If-Match: $CURRENT_VERSION" \
-H "Content-Type: application/json" \
-d '{"projectSlug":"hanzo-iam","environment":"prod","secretPath":"/","secretValue":"<new-secret>"}'
-d '{"value":"<new-secret>"}'
# 2. KMSSecret reconciles within 60s; force-roll the Job to pick up:
kubectl --context=do-sfo3-hanzo-k8s -n hanzo delete job iam-init-providers --ignore-not-found
@@ -156,7 +156,7 @@ export const getAnthropicSteps = (ctx: OnboardingComponentsContext): StepDefinit
file: 'Python',
code: dedent`
response = client.messages.create(
model="claude-3-opus-20240229",
model="zen5",
messages=[
{
"role": "user",
+3 -3
View File
@@ -116,8 +116,8 @@ Architecture, training methodology, and evaluation for the Zen frontier models
| [Zen Chain Of Thought](https://github.com/hanzoai/papers/blob/main/zen/zen-chain-of-thought.pdf) | `zen/zen-chain-of-thought.tex` |
| [Zen Code](https://github.com/hanzoai/papers/blob/main/zen/zen-code_whitepaper.pdf) | `zen/zen-code_whitepaper.tex` |
| [Zen Code Benchmarks](https://github.com/hanzoai/papers/blob/main/zen/zen-code-benchmarks.pdf) | `zen/zen-code-benchmarks.tex` |
| [Zen Coder](https://github.com/hanzoai/papers/blob/main/zen/zen-coder_whitepaper.pdf) | `zen/zen-coder_whitepaper.tex` |
| [Zen Coder Flash](https://github.com/hanzoai/papers/blob/main/zen/zen-coder-flash_whitepaper.pdf) | `zen/zen-coder-flash_whitepaper.tex` |
| [Zen Coder](https://github.com/hanzoai/papers/blob/main/zen/zen5-coder_whitepaper.pdf) | `zen/zen5-coder_whitepaper.tex` |
| [Zen Coder Flash](https://github.com/hanzoai/papers/blob/main/zen/zen5-coder-flash_whitepaper.pdf) | `zen/zen5-coder-flash_whitepaper.tex` |
| [Zen Context Extension](https://github.com/hanzoai/papers/blob/main/zen/zen-context-extension.pdf) | `zen/zen-context-extension.tex` |
| [Zen Designer Instruct](https://github.com/hanzoai/papers/blob/main/zen/zen-designer-instruct_whitepaper.pdf) | `zen/zen-designer-instruct_whitepaper.tex` |
| [Zen Designer Thinking](https://github.com/hanzoai/papers/blob/main/zen/zen-designer-thinking_whitepaper.pdf) | `zen/zen-designer-thinking_whitepaper.tex` |
@@ -151,7 +151,7 @@ Architecture, training methodology, and evaluation for the Zen frontier models
| [Zen Musician](https://github.com/hanzoai/papers/blob/main/zen/zen-musician.pdf) | `zen/zen-musician.tex` |
| [Zen Nano](https://github.com/hanzoai/papers/blob/main/zen/zen-nano_whitepaper.pdf) | `zen/zen-nano_whitepaper.tex` |
| [Zen Next](https://github.com/hanzoai/papers/blob/main/zen/zen-next_whitepaper.pdf) | `zen/zen-next_whitepaper.tex` |
| [Zen Omni](https://github.com/hanzoai/papers/blob/main/zen/zen-omni_whitepaper.pdf) | `zen/zen-omni_whitepaper.tex` |
| [Zen Omni](https://github.com/hanzoai/papers/blob/main/zen/zen5_whitepaper.pdf) | `zen/zen5_whitepaper.tex` |
| [Zen Privacy Federated](https://github.com/hanzoai/papers/blob/main/zen/zen-privacy-federated.pdf) | `zen/zen-privacy-federated.tex` |
| [Zen Pro](https://github.com/hanzoai/papers/blob/main/zen/zen-pro_whitepaper.pdf) | `zen/zen-pro_whitepaper.tex` |
| [Zen Quantization](https://github.com/hanzoai/papers/blob/main/zen/zen-quantization.pdf) | `zen/zen-quantization.tex` |
+3 -3
View File
@@ -102,7 +102,7 @@ await ast.recordInteraction({
role: 'assistant',
content: 'Quantum computing uses quantum bits (qubits) that can be 0, 1, or both at the same time...',
},
model: 'zen-coder-32b-instruct',
model: 'zen5-coder-32b-instruct',
tokens: { input: 42, output: 156 },
latencyMs: 1240,
},
@@ -224,7 +224,7 @@ app.post('/api/chat', async (req, res) => {
const { messages } = req.body;
const completion = await llm.chat.completions.create({
model: 'zen-coder-32b-instruct',
model: 'zen5-coder-32b-instruct',
messages,
});
@@ -284,7 +284,7 @@ AST stores all records in a normalized JSON format compatible with common fine-t
},
"output": {
"message": {"role": "assistant", "content": "..."},
"model": "zen-coder-32b-instruct",
"model": "zen5-coder-32b-instruct",
"tokens": {"input": 42, "output": 156},
"latency_ms": 1240
},
+2 -2
View File
@@ -45,7 +45,7 @@ int main() {
hanzo::Client client{std::getenv("HANZO_API_KEY")};
auto response = client.chat().completions().create({
.model = "zen-omni",
.model = "zen5",
.messages = {{"user", "Explain quantum computing"}},
});
@@ -59,7 +59,7 @@ int main() {
curl https://api.hanzo.ai/v1/chat/completions \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"zen-omni","messages":[{"role":"user","content":"Hello"}]}'
-d '{"model":"zen5","messages":[{"role":"user","content":"Hello"}]}'
```
## Resources
+2 -2
View File
@@ -48,7 +48,7 @@ func main() {
)
resp, err := client.Chat.Completions.New(context.TODO(), hanzoai.ChatCompletionNewParams{
Model: hanzoai.F("zen-omni"),
Model: hanzoai.F("zen5"),
Messages: hanzoai.F([]hanzoai.ChatCompletionMessageParamUnion{
hanzoai.UserMessage("Explain quantum computing"),
}),
@@ -64,7 +64,7 @@ func main() {
```go
stream := client.Chat.Completions.NewStreaming(ctx, hanzoai.ChatCompletionNewParams{
Model: hanzoai.F("zen-coder"),
Model: hanzoai.F("zen5-coder"),
Messages: hanzoai.F([]hanzoai.ChatCompletionMessageParamUnion{
hanzoai.UserMessage("Write a haiku about AI"),
}),
+3 -3
View File
@@ -115,7 +115,7 @@ from hanzoai import Hanzo
client = Hanzo(api_key="hk-your-key")
response = client.chat.completions.create(
model="zen-omni",
model="zen5",
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)
@@ -126,7 +126,7 @@ import Hanzo from '@hanzo/sdk';
const client = new Hanzo({ apiKey: 'hk-your-key' });
const response = await client.chat.completions.create({
model: 'zen-omni',
model: 'zen5',
messages: [{ role: 'user', content: 'Hello' }],
});
```
@@ -134,7 +134,7 @@ const response = await client.chat.completions.create({
```go tab="Go"
client := hanzoai.NewClient(option.WithAPIKey("hk-your-key"))
response, _ := client.Chat.Completions.New(ctx, hanzoai.ChatCompletionNewParams{
Model: hanzoai.F("zen-omni"),
Model: hanzoai.F("zen5"),
Messages: hanzoai.F([]hanzoai.ChatCompletionMessageParamUnion{
hanzoai.UserMessage("Hello"),
}),
+2 -2
View File
@@ -46,7 +46,7 @@ import ai.hanzo.Hanzo
val client = Hanzo(apiKey = System.getenv("HANZO_API_KEY"))
val response = client.chat.completions.create(
model = "zen-omni",
model = "zen5",
messages = listOf(Message.user("Explain quantum computing")),
)
println(response.choices.first().message.content)
@@ -58,7 +58,7 @@ println(response.choices.first().message.content)
val response = HttpClient().post("https://api.hanzo.ai/v1/chat/completions") {
header("Authorization", "Bearer $apiKey")
contentType(ContentType.Application.Json)
setBody("""{"model":"zen-omni","messages":[{"role":"user","content":"Hello"}]}""")
setBody("""{"model":"zen5","messages":[{"role":"user","content":"Hello"}]}""")
}
```
+5 -5
View File
@@ -49,7 +49,7 @@ from hanzoai import Hanzo
client = Hanzo(api_key="hk-your-key") # or reads HANZO_API_KEY
response = client.chat.completions.create(
model="zen-omni",
model="zen5",
messages=[{"role": "user", "content": "Explain quantum computing"}],
)
print(response.choices[0].message.content)
@@ -59,7 +59,7 @@ print(response.choices[0].message.content)
```python
stream = client.chat.completions.create(
model="zen-coder",
model="zen5-coder",
messages=[{"role": "user", "content": "Write a haiku about AI"}],
stream=True,
)
@@ -77,7 +77,7 @@ from hanzoai import AsyncHanzo
async def main():
client = AsyncHanzo(api_key="hk-your-key")
response = await client.chat.completions.create(
model="zen-omni",
model="zen5",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
@@ -93,7 +93,7 @@ top of the same cloud:
```python
from hanzo import Agent
agent = Agent(model="zen-omni", tools=["search", "shell", "browser"])
agent = Agent(model="zen5", tools=["search", "shell", "browser"])
result = agent.run("Find the latest release and summarize the changelog.")
print(result.output)
```
@@ -120,7 +120,7 @@ tools = [{
}]
response = client.chat.completions.create(
model="zen-omni",
model="zen5",
messages=[{"role": "user", "content": "Weather in Tokyo?"}],
tools=tools,
tool_choice="auto",
+2 -2
View File
@@ -50,7 +50,7 @@ async fn main() -> anyhow::Result<()> {
let response = client
.chat()
.completions()
.create("zen-omni", "Explain quantum computing")
.create("zen5", "Explain quantum computing")
.await?;
println!("{}", response.choices[0].message.content);
@@ -66,7 +66,7 @@ use futures::StreamExt;
let mut stream = client
.chat()
.completions()
.stream("zen-coder", "Write a haiku about AI")
.stream("zen5-coder", "Write a haiku about AI")
.await?;
while let Some(chunk) = stream.next().await {
+2 -2
View File
@@ -38,7 +38,7 @@ import Hanzo
let client = HanzoClient(apiKey: ProcessInfo.processInfo.environment["HANZO_API_KEY"]!)
let response = try await client.chat.completions.create(
model: "zen-omni",
model: "zen5",
messages: [.user("Explain quantum computing")]
)
print(response.choices.first?.message.content ?? "")
@@ -52,7 +52,7 @@ request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: [
"model": "zen-omni",
"model": "zen5",
"messages": [["role": "user", "content": "Hello"]],
])
let (data, _) = try await URLSession.shared.data(for: request)
+3 -3
View File
@@ -54,7 +54,7 @@ import Hanzo from '@hanzo/sdk';
const client = new Hanzo({ apiKey: process.env.HANZO_API_KEY });
const response = await client.chat.completions.create({
model: 'zen-omni',
model: 'zen5',
messages: [{ role: 'user', content: 'Explain quantum computing' }],
});
@@ -65,7 +65,7 @@ console.log(response.choices[0].message.content);
```typescript
const stream = await client.chat.completions.create({
model: 'zen-coder',
model: 'zen5-coder',
messages: [{ role: 'user', content: 'Write a haiku about AI' }],
stream: true,
});
@@ -80,7 +80,7 @@ for await (const chunk of stream) {
```typescript
import { Agent } from '@hanzo/ai';
const agent = new Agent({ model: 'zen-omni', tools: ['search', 'shell'] });
const agent = new Agent({ model: 'zen5', tools: ['search', 'shell'] });
const result = await agent.run('Summarize the latest release notes.');
console.log(result.output);
```
+3 -3
View File
@@ -82,7 +82,7 @@ import { Agent, Runtime } from '@hanzo/agents'
const agent = new Agent({
name: 'researcher',
model: 'claude-sonnet-4-5-20250929',
model: 'zen5',
brain: { org: 'startx' }, // wires Hanzo Brain
tools: ['search', 'fetch', 'brain.recall'],
})
@@ -102,7 +102,7 @@ from hanzo_agents import Agent, Runtime
agent = Agent(
name="researcher",
model="claude-sonnet-4-5-20250929",
model="zen5",
brain={"org": "startx"},
tools=["search", "fetch", "brain.recall"],
)
@@ -122,7 +122,7 @@ import agents "github.com/hanzoai/agents/sdk/go"
agent := agents.New(agents.Config{
Name: "researcher",
Model: "claude-sonnet-4-5-20250929",
Model: "zen5",
Brain: agents.Brain{Org: "startx"},
Tools: []string{"search", "fetch", "brain.recall"},
})
+133 -111
View File
@@ -50,158 +50,180 @@ The Analytics dashboard is available at `console.hanzo.ai`:
## Metrics API
Query usage and cost metrics programmatically:
Every call is on the one Hanzo API host, `https://api.hanzo.ai/v1/analytics`, with your normal bearer.
| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/v1/analytics/overview?range=` | One roll-up per lens for the range |
| `GET` | `/v1/analytics/timeseries?range=` | Bucketed series (requests, tokens, spend) |
| `GET` | `/v1/analytics/top?range=` | Leaderboards — top models, providers, pages |
| `GET` | `/v1/analytics/health` | Datastore + per-lens availability |
| `POST` | `/v1/analytics` | Capture one event |
| `POST` | `/v1/analytics/batch` | Capture a batch of events |
`range` accepts `24h`, `7d`, `30d`, … The org is never passed by hand: the gateway re-mints `X-Org-Id` from your validated token, and every response echoes the resolved `scope`.
### Overview
```bash
# Get usage summary for the last 7 days
curl https://cloud.hanzo.ai/v1/analytics/usage \
-H "Authorization: Bearer $HANZO_TOKEN" \
-d '{
"start_date": "2025-06-01",
"end_date": "2025-06-07",
"group_by": ["model", "project"],
"metrics": ["requests", "tokens", "cost"]
}'
curl -fsS "https://api.hanzo.ai/v1/analytics/overview?range=7d" \
-H "Authorization: Bearer $HANZO_API_KEY"
```
Response:
```json
{
"data": [
{
"model": "claude-sonnet-4-5-20250929",
"project": "platform",
"requests": 142500,
"input_tokens": 285000000,
"output_tokens": 71250000,
"cost": 854.25
}
],
"summary": {
"total_requests": 1200000,
"total_tokens": 847000000,
"total_cost": 2341.50
"range": "7d",
"start": "2026-07-18T22:21:04Z",
"end": "2026-07-25T22:21:04Z",
"interval": "day",
"scope": { "org": "hanzo" },
"llm": {
"available": true,
"requests": 8661,
"tokens": 130586947,
"promptTokens": 126558231,
"completionTokens": 4028716,
"spendCents": 206418,
"models": 115,
"providers": 7,
"errors": 212,
"errorRate": 0.024,
"source": "hanzo.cloud_usage"
},
"web": {
"available": true,
"pageviews": 4164,
"visitors": 1159,
"sessions": 875,
"source": "hanzo.events"
}
}
```
## Cost Tracking
Each lens carries `available` and the `source` table it read, so a zero is never ambiguous — you can tell "nothing happened" from "that lens is not wired up".
### Budgets
Set per-project spending limits with automatic enforcement:
### Time series
```bash
# Set a monthly budget
curl -X POST https://cloud.hanzo.ai/v1/analytics/budgets \
-H "Authorization: Bearer $HANZO_TOKEN" \
-d '{
"project_id": "proj_abc123",
"monthly_limit": 5000.00,
"alert_thresholds": [0.50, 0.75, 0.90],
"action_on_exceed": "soft_limit"
}'
curl -fsS "https://api.hanzo.ai/v1/analytics/timeseries?range=7d" \
-H "Authorization: Bearer $HANZO_API_KEY"
```
### Cost Breakdown
```json
{
"range": "7d",
"interval": "day",
"scope": { "org": "hanzo" },
"series": [
{ "t": "2026-07-18T00:00:00Z", "requests": 399, "tokens": 57306163, "spendCents": 88746 },
{ "t": "2026-07-19T00:00:00Z", "requests": 5165, "tokens": 11717502, "spendCents": 23348 }
]
}
```
| Dimension | Description |
|-----------|-------------|
| By model | Cost per LLM model (input/output tokens) |
| By project | Cost per project/team |
| By API key | Cost per individual API key |
| By endpoint | Cost per service endpoint |
| By time | Hourly, daily, weekly, monthly aggregation |
### Leaderboards
```bash
curl -fsS "https://api.hanzo.ai/v1/analytics/top?range=7d" \
-H "Authorization: Bearer $HANZO_API_KEY"
```
```json
{
"range": "7d",
"scope": { "org": "hanzo" },
"models": {
"available": true,
"items": [
{ "model": "zen5", "provider": "hanzo", "requests": 483, "tokens": 64757117, "spendCents": 98732, "pct": 48.2 }
]
}
}
```
### Health
```bash
curl -fsS https://api.hanzo.ai/v1/analytics/health
```
```json
{
"datastore": true,
"lenses": {
"events": { "available": true, "table": "hanzo.events" },
"llm": { "available": true, "table": "hanzo.cloud_usage" }
},
"service": "analytics",
"status": "ok",
"warehouse": "hanzo"
}
```
## Cost and budgets
Spend is metered into the same balance every product draws from, so cost lives with billing rather than in a second budgeting system:
- **Balance and itemized usage** — `GET /v1/billing/balance`, `GET /v1/billing/usage`
- **Spend alerts** — `GET` · `POST /v1/billing/spend-alerts`
- **Roll-ups by model, provider, and day** — the analytics endpoints above
See [Credits & Billing](/docs/billing).
### Cost breakdown dimensions
| Dimension | Where |
|-----------|-------|
| By model | `/v1/analytics/top` → `models.items[].spendCents` |
| By provider | `/v1/analytics/top` → `models.items[].provider` |
| By time | `/v1/analytics/timeseries` → `series[].spendCents` |
| By transaction | `/v1/billing/usage` → `usage[].metadata` |
## Operational Health
Monitor service health with real-time metrics:
Request rate, error rate, and latency for a running service come from the observability lens, not this one — see [Hanzo o11y](/docs/services/o11y):
```bash
curl -fsS "https://api.hanzo.ai/v1/o11y/metrics?product=cloud" \
-H "Authorization: Bearer $HANZO_API_KEY"
```
| Metric | Description | Alert Threshold |
|--------|-------------|-----------------|
| `error_rate` | Percentage of 4xx/5xx responses | > 1% |
| `latency_p50` | Median response time | > 200ms |
| `latency_p99` | 99th percentile response time | > 2000ms |
| `saturation` | Queue depth / capacity ratio | > 0.8 |
| `availability` | Successful requests / total requests | < 99.9% |
## Alerting
Configure alerts that trigger on metric thresholds:
```bash
# Create an alert rule
curl -X POST https://cloud.hanzo.ai/v1/analytics/alerts \
-H "Authorization: Bearer $HANZO_TOKEN" \
-d '{
"name": "High error rate",
"metric": "error_rate",
"condition": "gt",
"threshold": 0.01,
"window": "5m",
"channels": [
{"type": "slack", "webhook": "https://hooks.slack.com/..."},
{"type": "pagerduty", "routing_key": "..."}
]
}'
```
## Data Export
Export analytics data for external BI tools:
```bash
# Export as CSV
curl "https://cloud.hanzo.ai/v1/analytics/export?format=csv&range=30d" \
-H "Authorization: Bearer $HANZO_TOKEN" \
-o analytics-export.csv
# Stream to webhook (real-time)
curl -X POST https://cloud.hanzo.ai/v1/analytics/streams \
-H "Authorization: Bearer $HANZO_TOKEN" \
-d '{
"destination": "https://your-bi-tool.com/ingest",
"events": ["request.completed", "cost.updated"],
"format": "json"
}'
```
## SDK Integration
The analytics endpoints are plain REST on `api.hanzo.ai`, so any HTTP client works — no analytics-specific SDK to install.
### Python
```python
from hanzoai import Hanzo
import httpx, os
client = Hanzo(api_key="your-key")
# Get usage for current billing period
usage = client.analytics.usage(
group_by=["model"],
metrics=["requests", "tokens", "cost"]
r = httpx.get(
"https://api.hanzo.ai/v1/analytics/top",
params={"range": "7d"},
headers={"Authorization": f"Bearer {os.environ['HANZO_API_KEY']}"},
)
for row in usage.data:
print(f"{row.model}: {row.requests} requests, ${row.cost:.2f}")
for row in r.json()["models"]["items"]:
print(f"{row['model']}: {row['requests']} requests, ${row['spendCents'] / 100:.2f}")
```
### TypeScript
```typescript
import Hanzo from '@hanzo/ai'
const client = new Hanzo({ apiKey: 'your-key' })
const usage = await client.analytics.usage({
groupBy: ['model', 'project'],
metrics: ['requests', 'tokens', 'cost'],
range: '7d'
const res = await fetch('https://api.hanzo.ai/v1/analytics/top?range=7d', {
headers: { Authorization: `Bearer ${process.env.HANZO_API_KEY}` },
})
const { models } = await res.json()
usage.data.forEach(row => {
console.log(`${row.model}: ${row.requests} requests, $${row.cost}`)
})
for (const row of models.items) {
console.log(`${row.model}: ${row.requests} requests, $${(row.spendCents / 100).toFixed(2)}`)
}
```
## Related Services
+1 -1
View File
@@ -148,7 +148,7 @@ endpoints:
apiKey: "${ANTHROPIC_API_KEY}"
models:
default:
- claude-sonnet-4-5-20250929
- zen5
- claude-opus-4-20250514
```
@@ -336,7 +336,6 @@ The Hanzo cluster is exposed via an nginx Ingress with TLS (cert-manager + Let's
| Domain | Type | Target |
|--------|------|--------|
| `api.hanzo.ai` | A (CF proxied) | `24.199.76.156` (hanzo-k8s LB) |
| `llm.hanzo.ai` | CNAME | `api.hanzo.ai` |
| `api.lux.network` | A | `134.199.141.71` (lux-k8s LB) |
### Operations
@@ -9,7 +9,7 @@ The Hanzo IAM web UI is a React [SPA](https://developer.mozilla.org/en-US/docs/G
- Hanzo IAM SDKs (e.g. iam-go-sdk)
- Your own applications and scripts
**API reference:** [https://iam.hanzo.ai/swagger](https://iam.hanzo.ai/swagger). To regenerate the Swagger spec, see [Developer guide Swagger](/docs/developer-guide/swagger#generate-swagger-files).
**API reference:** [https://hanzo.id/swagger](https://hanzo.id/swagger). To regenerate the Swagger spec, see [Developer guide Swagger](/docs/developer-guide/swagger#generate-swagger-files).
## Response language
@@ -17,7 +17,7 @@ Responses can be localized. Send the `Accept-Language` header to get error messa
```bash
# Example: Get error messages in French
curl -X GET https://iam.hanzo.ai/v1/iam/get-account \
curl -X GET https://api.hanzo.ai/v1/iam/get-account \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Accept-Language: fr"
@@ -52,7 +52,7 @@ Use the access token obtained after a user signs in (e.g. from the OAuth code ex
#### Getting the token
The app receives the token at the end of the OAuth login flow (code + state). Issued tokens are also visible in the Hanzo IAM UI (**Tokens** page, e.g. `https://iam.hanzo.ai/tokens`).
The app receives the token at the end of the OAuth login flow (code + state). Issued tokens are also visible in the Hanzo IAM UI (**Tokens** page, e.g. `https://hanzo.id/tokens`).
Example (Go, iam-go-sdk):
@@ -97,7 +97,7 @@ func (c *ApiController) Signin() {
/page?access_token=<The access token>
```
Demo site example: `https://iam.hanzo.ai/api/get-global-providers?access_token=eyJhbGciOiJSUzI1NiIs`
Demo site example: `https://api.hanzo.ai/v1/iam/get-global-providers?access_token=eyJhbGciOiJSUzI1NiIs`
2. **Bearer header:**
@@ -111,7 +111,7 @@ Use this for **machine-to-machine** calls (no user). Permissions are those of th
#### Getting credentials
On the application edit page (e.g. `https://iam.hanzo.ai/applications/casbin/app-vue-python-example`) youll see **Client ID** and **Client Secret**.
On the application edit page (e.g. `https://hanzo.id/applications/casbin/app-vue-python-example`) youll see **Client ID** and **Client Secret**.
#### Use cases
@@ -189,12 +189,12 @@ Create a key pair on the users account settings page.
/page?accessKey=<The user's access key>&accessSecret=<the user's access secret>"
```
Example: `https://iam.hanzo.ai/api/get-global-providers?accessKey=...&accessSecret=...`
Example: `https://api.hanzo.ai/v1/iam/get-global-providers?accessKey=...&accessSecret=...`
![User Api Key](/img/basic/user_api_key.png)
```bash
curl --location 'http://iam.hanzo.ai/api/user?accessKey=b86db9dc-6bd7-4997-935c-af480dd2c796&accessSecret=79911517-fc36-4093-b115-65a9741f6b14'
curl --location 'https://api.hanzo.ai/v1/iam/user?accessKey=b86db9dc-6bd7-4997-935c-af480dd2c796&accessSecret=79911517-fc36-4093-b115-65a9741f6b14'
### 4. Username and password
@@ -212,12 +212,12 @@ Username format: `<organization>/<username>`. API calls run as that user.
## SSO logout
The `/api/sso-logout` endpoint logs a user out from all applications or only the current session, depending on `logoutAll`.
The `/v1/iam/sso-logout` endpoint logs a user out from all applications or only the current session, depending on `logoutAll`.
### Endpoint
```http
GET or POST /api/sso-logout?logoutAll=<true|false>
GET or POST /v1/iam/sso-logout?logoutAll=<true|false>
### Parameters
@@ -247,15 +247,15 @@ The user must be authenticated. Use any of the [authentication methods](#how-to-
```bash
# Full SSO logout (all sessions)
curl -X POST https://iam.hanzo.ai/api/sso-logout \
curl -X POST https://api.hanzo.ai/v1/iam/sso-logout \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# Session-level logout (current session only)
curl -X POST "https://iam.hanzo.ai/api/sso-logout?logoutAll=false" \
curl -X POST "https://api.hanzo.ai/v1/iam/sso-logout?logoutAll=false" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# Using session cookie
curl -X POST https://iam.hanzo.ai/api/sso-logout \
curl -X POST https://api.hanzo.ai/v1/iam/sso-logout \
--cookie "iam_session_id=abc123def456"
### Response
@@ -114,7 +114,7 @@ insights.capture({
event: 'api_request',
properties: {
endpoint: '/v1/chat/completions',
model: 'zen-coder-32b-instruct',
model: 'zen5-coder-32b-instruct',
tokens: 1500,
latency_ms: 340,
},
@@ -249,7 +249,7 @@ insights.capture('purchase_completed', {
// AI/LLM
insights.capture('model_inference', {
model: 'zen-coder-32b-instruct',
model: 'zen5-coder-32b-instruct',
tokens_in: 150,
tokens_out: 500,
latency_ms: 1240,
+16 -11
View File
@@ -11,7 +11,7 @@ Hanzo KMS is the key management service for the Hanzo platform. It stores secret
| | |
|---|---|
| **API** | `https://kms.hanzo.ai` |
| **API** | `https://api.hanzo.ai` — one host for every Hanzo service |
| **API prefix** | `/v1/kms` |
| **Auth** | Machine-identity login → bearer JWT (verified against [Hanzo IAM](/docs/services/iam)) |
| **K8s sync** | `KMSSecret` CRD via the kms-operator |
@@ -27,7 +27,7 @@ KMS uses **machine identities**: a non-human `clientId` + `clientSecret` minted
```bash
# 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 \
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)
@@ -47,7 +47,7 @@ A secret is addressed by four coordinates:
```
org / path / name ? env
hanzo / providers / OPENAI_API_KEY ? env=production
hanzo / providers / OPENAI_API_KEY ? env=prod
```
- **org** — the organization slug; must match the token's `owner` claim.
@@ -56,24 +56,29 @@ hanzo / providers / OPENAI_API_KEY ? env=production
- **env** — a query parameter; defaults to `default` when omitted.
<Callout type="info">
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).
Reads are **exact-name**: `GET …/secrets/{path}/{name}?env=` returns one value. `GET …/secrets?env=` lists a namespace's secret **metadata** (name, path, env, scheme) — never values. Enumerate the keys your workload needs; the `KMSSecret` CRD does exactly this.
</Callout>
## Reading & writing secrets
All routes are under `https://kms.hanzo.ai/v1/kms/orgs/{org}/secrets`.
All routes are under `https://api.hanzo.ai/v1/kms/orgs/{org}/secrets`.
| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/v1/kms/orgs/{org}/secrets?env=` | List secret metadata in the namespace (no values) |
| `GET` | `/v1/kms/orgs/{org}/secrets/{path}/{name}?env=` | Read a secret value + version |
| `POST` | `/v1/kms/orgs/{org}/secrets` | Create/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 |
<Callout type="warn">
`env` is **required** on every read and write. Omitting it silently buckets the secret into the `default` environment, where the workload looking for `env=prod` will never find it. Always pass it explicitly.
</Callout>
### Read
```bash
curl -sS "https://kms.hanzo.ai/v1/kms/orgs/hanzo/secrets/providers/OPENAI_API_KEY?env=production" \
curl -sS "https://api.hanzo.ai/v1/kms/orgs/hanzo/secrets/providers/OPENAI_API_KEY?env=prod" \
-H "Authorization: Bearer $ACCESS_TOKEN"
```
@@ -81,15 +86,15 @@ curl -sS "https://kms.hanzo.ai/v1/kms/orgs/hanzo/secrets/providers/OPENAI_API_KE
{ "secret": { "value": "<secret value>" }, "version": 3 }
```
A root-path secret omits the folder segment: `…/secrets/STRIPE_SECRET_KEY?env=production`.
A root-path secret omits the folder segment: `…/secrets/STRIPE_SECRET_KEY?env=prod`.
### Create
```bash
curl -sS -X POST "https://kms.hanzo.ai/v1/kms/orgs/hanzo/secrets" \
curl -sS -X POST "https://api.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>" }'
-d '{ "path": "/providers", "name": "OPENAI_API_KEY", "env": "prod", "value": "<new value>" }'
```
```json
@@ -101,7 +106,7 @@ curl -sS -X POST "https://kms.hanzo.ai/v1/kms/orgs/hanzo/secrets" \
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.
```bash
curl -sS -X PATCH "https://kms.hanzo.ai/v1/kms/orgs/hanzo/secrets/providers/OPENAI_API_KEY?env=production" \
curl -sS -X PATCH "https://api.hanzo.ai/v1/kms/orgs/hanzo/secrets/providers/OPENAI_API_KEY?env=prod" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "If-Match: 3" \
-H "Content-Type: application/json" \
@@ -141,7 +146,7 @@ spec:
secretNamespace: hanzo
secretsScope:
projectSlug: hanzo # -> {org}
envSlug: production # -> ?env=production
envSlug: production # -> ?env=prod
secretsPath: /providers # -> {path}
keys: # enumerated; no list endpoint
- OPENAI_API_KEY
@@ -89,7 +89,7 @@ A machine identity is a client-credentials application in [Hanzo IAM](/docs/serv
Once you have a `clientId`/`clientSecret`, log in to KMS:
```bash
curl -X POST https://kms.hanzo.ai/v1/kms/auth/login \
curl -X POST https://api.hanzo.ai/v1/kms/auth/login \
-H "Content-Type: application/json" \
-d "{\"clientId\":\"$KMS_CLIENT_ID\",\"clientSecret\":\"$KMS_CLIENT_SECRET\"}"
```
+78 -74
View File
@@ -9,9 +9,10 @@ description: Full-stack observability platform — Prometheus metrics, Grafana d
Hanzo O11y is the unified observability stack for the entire Hanzo platform. It collects metrics, logs, and distributed traces from every service, aggregates them into a single pane of glass, and drives alerting and SLO enforcement. Built on Prometheus, Grafana, and OpenTelemetry, O11y gives operators and developers real-time visibility into infrastructure health, application performance, and service mesh telemetry.
**Endpoint:** `o11y.hanzo.ai`
**Prometheus:** `o11y.hanzo.ai:9090`
**Gateway:** `api.hanzo.ai/v1/o11y/*`
**API:** `https://api.hanzo.ai/v1/o11y` — one host, one bearer, like every other Hanzo service.
**Dashboards:** `o11y.hanzo.ai`, which 301-redirects to `console.hanzo.ai/o11y`.
There is no separately-addressable Prometheus, Loki, or OTLP port on the public internet: `o11y.hanzo.ai:9090` and `:4317` are closed. Every read and every write goes through `/v1/o11y`.
## Features
@@ -86,38 +87,64 @@ Hanzo O11y is the unified observability stack for the entire Hanzo platform. It
### Send Metrics via OTLP
```bash
# Push metrics using the OpenTelemetry HTTP endpoint
curl -X POST https://o11y.hanzo.ai/v1/metrics \
# Ingest telemetry
curl -X POST https://api.hanzo.ai/v1/o11y/ingestion \
-H "Authorization: Bearer $HANZO_TOKEN" \
-H "Content-Type: application/x-protobuf" \
--data-binary @metrics.pb
-H "Content-Type: application/json" \
--data-binary @batch.json
# → {"accepted":1,"dropped":0}
```
# Or query Prometheus directly
curl "https://o11y.hanzo.ai:9090/api/v1/query?query=up" \
### Read a service's health
`product` names the service you are asking about, and is required:
```bash
curl -fsS "https://api.hanzo.ai/v1/o11y/status?product=cloud" \
-H "Authorization: Bearer $HANZO_TOKEN"
```
### Query Logs
```bash
curl "https://o11y.hanzo.ai/loki/api/v1/query_range" \
-H "Authorization: Bearer $HANZO_TOKEN" \
--data-urlencode 'query={service="gateway"} |= "error"' \
--data-urlencode 'start=1708560000' \
--data-urlencode 'end=1708646400' \
--data-urlencode 'limit=100'
```json
{ "product": "cloud", "up": true, "latencyMs": 12, "deployments": [], "checkedAt": "2026-07-25T21:56:23Z" }
```
### Instrument Your Application
### Query metrics, logs, and traces
```bash
# Set environment variables for any OTLP-compatible application
export OTEL_EXPORTER_OTLP_ENDPOINT=https://o11y.hanzo.ai:4317
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $HANZO_TOKEN"
export OTEL_SERVICE_NAME=my-service
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production"
# Request/error/latency series for one product
curl -fsS "https://api.hanzo.ai/v1/o11y/metrics?product=cloud" \
-H "Authorization: Bearer $HANZO_TOKEN"
# Recent log lines
curl -fsS "https://api.hanzo.ai/v1/o11y/logs?product=cloud&limit=100" \
-H "Authorization: Bearer $HANZO_TOKEN"
# Traces
curl -fsS "https://api.hanzo.ai/v1/o11y/traces" \
-H "Authorization: Bearer $HANZO_TOKEN"
# Raw PromQL
curl -fsS "https://api.hanzo.ai/v1/o11y/query?query=up" \
-H "Authorization: Bearer $HANZO_TOKEN"
curl -fsS "https://api.hanzo.ai/v1/o11y/query_range?query=sum(up)&start=1&end=2&step=15" \
-H "Authorization: Bearer $HANZO_TOKEN"
```
### Endpoints
| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/v1/o11y/health` | Liveness |
| `GET` | `/v1/o11y/status?product=` | Up/down, latency, live deployments |
| `GET` | `/v1/o11y/metrics?product=` | Request, error, and latency series |
| `GET` | `/v1/o11y/logs?product=` | Log lines with a cursor |
| `GET` | `/v1/o11y/traces` | Trace list |
| `GET` | `/v1/o11y/query` · `/v1/o11y/query_range` | PromQL instant / range |
| `POST` | `/v1/o11y/ingestion` | Telemetry ingest |
| `GET` | `/v1/o11y/sessions` · `/v1/o11y/annotation-queues` | Session replay, review queues |
`/v1/o11y/vm/*` is fleet-wide platform infrastructure health and is restricted to platform administrators — a normal org bearer gets `403`.
## Metrics
### Prometheus Collection
@@ -187,8 +214,8 @@ inference_requests.labels(model="qwen3-4b", status="ok").inc()
O11y aggregates logs from all Hanzo services via Loki. Logs are structured as JSON and enriched with K8s metadata (namespace, pod, container, node) automatically.
```bash
# Push logs directly via the Loki API
curl -X POST https://o11y.hanzo.ai/loki/api/v1/push \
# Push logs through the one o11y door
curl -X POST https://api.hanzo.ai/v1/o11y/ingestion \
-H "Authorization: Bearer $HANZO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
@@ -233,12 +260,8 @@ sum by (service) (rate({namespace="hanzo"} [5m]))
O11y collects distributed traces via the OpenTelemetry Collector and stores them in Tempo. Traces automatically propagate across service boundaries using W3C Trace Context headers.
```bash
# Query traces by service
curl "https://o11y.hanzo.ai/tempo/api/search?service.name=gateway&limit=20" \
-H "Authorization: Bearer $HANZO_TOKEN"
# Get a specific trace by ID
curl "https://o11y.hanzo.ai/tempo/api/traces/abc123def456" \
# Query traces
curl -fsS "https://api.hanzo.ai/v1/o11y/traces" \
-H "Authorization: Bearer $HANZO_TOKEN"
```
@@ -252,7 +275,7 @@ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExport
# Configure exporter
exporter = OTLPSpanExporter(
endpoint="https://o11y.hanzo.ai:4317",
endpoint="https://api.hanzo.ai/v1/o11y/ingestion",
headers={"Authorization": f"Bearer {HANZO_TOKEN}"}
)
@@ -356,54 +379,35 @@ O11y ships with curated Grafana dashboards for every layer of the stack:
### Custom Dashboards
Create dashboards via the Grafana API or UI at `o11y.hanzo.ai`:
Build dashboards in the console at [console.hanzo.ai/o11y](https://console.hanzo.ai/o11y) (`o11y.hanzo.ai` redirects there). A dashboard is a saved set of `/v1/o11y` queries — the panel definition below is the shape the console stores:
```bash
# Import a dashboard from JSON
curl -X POST https://o11y.hanzo.ai/api/dashboards/db \
-H "Authorization: Bearer $GRAFANA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"dashboard": {
"title": "My Service Dashboard",
"panels": [
{
"title": "Request Rate",
"type": "timeseries",
"targets": [
{"expr": "rate(http_requests_total{service=\"my-service\"}[5m])"}
]
}
]
},
"overwrite": false
}'
```json
{
"dashboard": {
"title": "My Service Dashboard",
"panels": [
{
"title": "Request Rate",
"type": "timeseries",
"targets": [
{ "expr": "rate(http_requests_total{service=\"my-service\"}[5m])" }
]
}
]
}
}
```
## SLO Management
Define Service Level Objectives and track error budgets:
SLOs are defined in the console at [console.hanzo.ai/o11y](https://console.hanzo.ai/o11y). An SLO is a saved SLI query plus a target and a window; the live alert state it drives is readable over the API:
```bash
# Create an SLO
curl -X POST https://api.hanzo.ai/v1/o11y/slos \
-H "Authorization: Bearer $HANZO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Gateway Availability",
"description": "99.9% of requests return non-5xx in a 30-day window",
"sli": {
"type": "availability",
"good": "http_requests_total{service=\"gateway\",status!~\"5..\"}",
"total": "http_requests_total{service=\"gateway\"}"
},
"target": 0.999,
"window": "30d",
"alerts": {
"burn_rate_1h": 14.4,
"burn_rate_6h": 6.0
}
}'
curl -fsS https://api.hanzo.ai/v1/o11y/alerts \
-H "Authorization: Bearer $HANZO_TOKEN"
# → {"status":"success","data":[]}
```
| SLO | Target | SLI | Error Budget (30d) |
@@ -450,7 +454,7 @@ Sidecar proxies automatically emit RED metrics (Rate, Errors, Duration) for all
| Variable | Description | Default |
|----------|-------------|---------|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint | `https://o11y.hanzo.ai:4317` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP ingest endpoint | `https://api.hanzo.ai/v1/o11y/ingestion` |
| `OTEL_EXPORTER_OTLP_HEADERS` | Auth headers for OTLP | - |
| `OTEL_SERVICE_NAME` | Service name for telemetry | - |
| `OTEL_RESOURCE_ATTRIBUTES` | Additional resource attributes | - |
+8 -7
View File
@@ -1,18 +1,19 @@
---
title: Hanzo PaaS
description: The platform.hanzo.ai control plane for deploying and operating services on Hanzo's Kubernetes clusters.
description: The Hanzo control plane for deploying and operating services on Hanzo's Kubernetes clusters — Studio at platform.hanzo.ai, API at api.hanzo.ai/v1/platform.
---
# Hanzo PaaS
> **API reference** · [Hanzo PaaS API →](/docs/openapi/paas) — every endpoint, generated from the OpenAPI spec.
Hanzo PaaS is the deployment and operations control plane for the Hanzo platform, served at **`platform.hanzo.ai`**. You register an application, point it at a container image or Git repository, attach a domain, and the platform builds, deploys, and operates it on Hanzo's Kubernetes clusters — with deployment history, rolling updates, and drift detection.
Hanzo PaaS is the deployment and operations control plane for the Hanzo platform. The Studio (browser UI) is at **`platform.hanzo.ai`**; every programmatic call goes to **`api.hanzo.ai/v1/platform`**, the one Hanzo API host. You register an application, point it at a container image or Git repository, attach a domain, and the platform builds, deploys, and operates it on Hanzo's Kubernetes clusters — with deployment history, rolling updates, and drift detection.
| | |
|---|---|
| **Control plane** | `https://platform.hanzo.ai` |
| **Canonical API** | `https://platform.hanzo.ai/v1` |
| **Studio** (browser UI) | `https://platform.hanzo.ai` |
| **API** | `https://api.hanzo.ai/v1/platform` |
| **Build** | `POST https://api.hanzo.ai/v1/runner` |
| **Auth** | [Hanzo IAM](/docs/services/iam) (interactive) · API key / service token (machine) |
| **Secrets** | [Hanzo KMS](/docs/services/kms) via `KMSSecret` CRDs — zero plaintext |
| **Ingress** | Hanzo Ingress + cert-manager (automatic Let's Encrypt TLS) |
@@ -20,8 +21,8 @@ Hanzo PaaS is the deployment and operations control plane for the Hanzo platform
## How it fits together
```
push image / git ──▶ platform.hanzo.ai ──▶ operator ──▶ Kubernetes
(declare + drive) (reconcile) (run + serve)
push image / git ──▶ api.hanzo.ai/v1 ──▶ operator ──▶ Kubernetes
(declare + drive) (reconcile) (run + serve)
▲ │
└────────── observe (drift) ────────┘
```
@@ -34,7 +35,7 @@ Secrets are never embedded in manifests — workloads read them from [Hanzo KMS]
## Get started
The full control-plane reference — the project/environment/application model, the `/v1` container redeploy, the build pipeline, clusters, domains, and the observe/drift board — lives in the Platform docs:
The full control-plane reference — the project/application model, `/v1/platform` deploys, `/v1/runner` builds, clusters, and domains — lives in the Platform docs:
<Cards>
<Card title="Platform Overview" href="/docs/services/platform">
@@ -3,13 +3,14 @@ title: Authentication
description: IAM tokens, API keys, and service tokens for the Hanzo PaaS API
---
Hanzo PaaS has three credential types, matched to its two API surfaces. Machine credentials are stored in [Hanzo KMS](/docs/services/kms) and read at runtime never hard-coded.
Hanzo PaaS has **one** API surface — `https://api.hanzo.ai/v1/platform` — and one credential shape: a bearer token. Machine credentials live in [Hanzo KMS](/docs/services/kms) and are read at runtime, never hard-coded.
| Credential | Header | Surface | Use |
|------------|--------|---------|-----|
| **IAM token** | `Authorization: Bearer <jwt>` | `/api` | Interactive / user access via SSO |
| **API key** | `x-api-key: <key>` | `/api` | Machine access to the build pipeline; org-scoped |
| **Service token** | `Authorization: Bearer <token>` | `/v1` | Container redeploy and observe (M2M) |
| Credential | Header | Use |
|------------|--------|-----|
| **IAM token** | `Authorization: Bearer <jwt>` | Everything — interactive SSO *and* machine access. An org-admin IAM login also authorizes `POST /v1/runner` builds. |
| **Build token** | `Authorization: Bearer <build-token>` | Fabric automation only (git-push-to-deploy, self-release). Never held by a user. |
There is no `x-api-key` header and no separate service token.
## OAuth2 via Hanzo ID
@@ -59,41 +60,32 @@ curl -X POST https://hanzo.id/v1/iam/oauth/token \
Refresh with `grant_type=refresh_token`. See [Hanzo IAM](/docs/services/iam) for the full flow.
## API keys (build pipeline)
## Machine identity (no user present)
API keys authenticate machine callers to the `/api` build-pipeline surface. Create one in the Studio under **Settings → API Keys** — the key is scoped to the organization you create it under and shown once.
<Callout type="warn">
Store API keys in [Hanzo KMS](/docs/services/kms) immediately. They are shown only at creation. If lost, revoke and create a new one.
</Callout>
A service exchanges its KMS machine identity for the same kind of bearer — no second credential type, no key file:
```bash
curl https://platform.hanzo.ai/api/application.all \
-H "x-api-key: $PLATFORM_API_KEY"
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)
```
## Service tokens (`/v1` surface)
The canonical `/v1` control surface (container redeploy, `/v1/apps` observe) uses a service token presented as a bearer:
```bash
curl -fsS -X POST \
"https://platform.hanzo.ai/v1/org/$ORG_ID/project/$PROJECT_ID/env/$ENV_ID/container/$CONTAINER_ID/redeploy" \
-H "Authorization: Bearer $PAAS_SERVICE_TOKEN"
```
The service token is issued out-of-band and stored in KMS (e.g. `kms.hanzo.ai/v1/kms/orgs/hanzo/secrets/paas/SERVICE_TOKEN`). It is a pure machine-to-machine credential with no user session.
`clientId` / `clientSecret` come from a Kubernetes Secret or your secret store — never inline them. The response is `{"accessToken":"<RS256 JWT>","expiresIn":604800,"tokenType":"Bearer"}`.
## Request examples
```bash
# List organizations (build pipeline, API key)
curl https://platform.hanzo.ai/api/organization.all \
-H "x-api-key: $PLATFORM_API_KEY"
# List projects
curl -fsS https://api.hanzo.ai/v1/platform/projects \
-H "Authorization: Bearer $TOKEN"
# Register an application
curl -X POST https://platform.hanzo.ai/api/application.create \
-H "x-api-key: $PLATFORM_API_KEY" \
# Create an application
curl -fsS -X POST https://api.hanzo.ai/v1/platform/projects/$PROJECT/apps \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "web", "environmentId": "'$ENV_ID'" }'
-d '{ "name": "web" }'
# Roll it forward
curl -fsS -X POST https://api.hanzo.ai/v1/platform/projects/$PROJECT/apps/web/deploy \
-H "Authorization: Bearer $TOKEN"
```
@@ -1,227 +1,66 @@
---
title: Clusters API
description: Manage Kubernetes and Docker Swarm clusters via the API
description: List, attach, scale, and detach Kubernetes clusters through api.hanzo.ai/v1.
---
The Clusters API handles the full lifecycle of compute clusters: creation, configuration, scaling, and deletion.
Clusters are a top-level noun on the one Hanzo API host. Managed clusters (Visor-provisioned DOKS/AWS/…) and bring-your-own clusters attached with a kubeconfig surface together.
All calls take an IAM bearer — see [Authentication](/docs/services/platform/api/authentication).
## Endpoints
| Procedure | Method | Description |
|-----------|--------|-------------|
| `cluster.all` | GET | List all clusters in the organization |
| `cluster.one` | GET | Get cluster details |
| `cluster.create` | POST | Provision a new cluster |
| `cluster.update` | PATCH | Update cluster settings |
| `cluster.remove` | DELETE | Destroy a cluster |
| `cluster.nodes` | GET | List nodes in a cluster |
| `cluster.addNode` | POST | Add a node to a cluster |
| `cluster.removeNode` | DELETE | Remove a node from a cluster |
| `cluster.grantAccess` | POST | Grant user access to a cluster |
| `cluster.listAccess` | GET | List cluster access permissions |
| `cluster.revokeAccess` | DELETE | Revoke user access |
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/clusters` | List every cluster the org can see (managed + BYO) |
| `POST` | `/v1/clusters` | Attach an existing cluster by kubeconfig |
| `DELETE` | `/v1/clusters/{id}` | Detach a cluster |
| `POST` | `/v1/clusters/{clusterId}/pools` | Create a node pool |
| `POST` | `/v1/clusters/{clusterId}/pools/{poolId}/scale` | Scale a node pool |
| `DELETE` | `/v1/clusters/{clusterId}/pools/{poolId}` | Delete a node pool |
| `GET` | `/v1/k8s/clusters` | Managed-cluster lifecycle view (DOKS detail + nodes) |
## List Clusters
Reads are org-scoped; create and delete are admin-gated, because they spend real infrastructure budget.
`GET /api/cluster.all?organizationId=org_abc123`
## List clusters
```bash
curl "https://platform.hanzo.ai/api/cluster.all?organizationId=org_abc123" \
-H "Authorization: Bearer YOUR_TOKEN"
curl -fsS https://api.hanzo.ai/v1/clusters \
-H "Authorization: Bearer $TOKEN"
```
**Response:**
```json
{
"result": {
"data": [
{
"id": "cluster_abc123",
"name": "production",
"provider": "digitalocean",
"region": "sfo3",
"orchestrator": "kubernetes",
"status": "running",
"nodeCount": 3,
"createdAt": "2025-06-01T10:00:00Z"
},
{
"id": "cluster_def456",
"name": "staging",
"provider": "hetzner",
"region": "nbg1",
"orchestrator": "swarm",
"status": "running",
"nodeCount": 1,
"createdAt": "2025-08-15T14:00:00Z"
}
]
}
}
{ "clusters": [] }
```
## Get Cluster
`GET /api/cluster.one?clusterId=cluster_abc123`
## Attach a cluster
```bash
curl "https://platform.hanzo.ai/api/cluster.one?clusterId=cluster_abc123" \
-H "Authorization: Bearer YOUR_TOKEN"
```
**Response:**
```json
{
"result": {
"data": {
"id": "cluster_abc123",
"name": "production",
"provider": "digitalocean",
"region": "sfo3",
"orchestrator": "kubernetes",
"status": "running",
"nodeCount": 3,
"nodes": [
{
"id": "node_001",
"role": "manager",
"plan": "medium",
"ip": "203.0.113.10",
"status": "running"
},
{
"id": "node_002",
"role": "worker",
"plan": "small",
"ip": "203.0.113.11",
"status": "running"
}
],
"createdAt": "2025-06-01T10:00:00Z"
}
}
}
```
## Create Cluster
`POST /api/cluster.create`
```bash
curl -X POST https://platform.hanzo.ai/api/cluster.create \
-H "Authorization: Bearer YOUR_TOKEN" \
curl -fsS -X POST https://api.hanzo.ai/v1/clusters \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_abc123",
"name": "production",
"provider": "digitalocean",
"region": "sfo3",
"orchestrator": "kubernetes",
"nodes": [
{ "role": "manager", "plan": "medium", "count": 1 },
{ "role": "worker", "plan": "small", "count": 2 }
]
}'
-d '{ "name": "prod", "kubeconfig": "'"$(base64 < ~/.kube/config)"'" }'
```
**Parameters:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `organizationId` | string | Yes | Organization ID |
| `name` | string | Yes | Cluster name |
| `provider` | string | Yes | `digitalocean`, `hetzner`, or `aws` |
| `region` | string | Yes | Provider region code |
| `orchestrator` | string | Yes | `kubernetes` or `swarm` |
| `nodes` | array | Yes | Node configuration |
**Node object:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `role` | string | Yes | `manager` or `worker` |
| `plan` | string | Yes | VM plan (nano, micro, small, medium, power) |
| `count` | number | Yes | Number of nodes |
**Response:** `201 Created`
Cluster provisioning is asynchronous. The cluster enters `provisioning` status and transitions to `running` once all nodes are ready (typically 2-5 minutes).
## Update Cluster
`PATCH /api/cluster.update`
## Scale a node pool
```bash
curl -X PATCH https://platform.hanzo.ai/api/cluster.update \
-H "Authorization: Bearer YOUR_TOKEN" \
curl -fsS -X POST \
"https://api.hanzo.ai/v1/clusters/$CLUSTER_ID/pools/$POOL_ID/scale" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clusterId": "cluster_abc123",
"name": "production-us-east"
}'
-d '{ "count": 5 }'
```
## Add Node
`POST /api/cluster.addNode`
## Detach a cluster
```bash
curl -X POST https://platform.hanzo.ai/api/cluster.addNode \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clusterId": "cluster_abc123",
"role": "worker",
"plan": "small"
}'
curl -fsS -X DELETE "https://api.hanzo.ai/v1/clusters/$CLUSTER_ID" \
-H "Authorization: Bearer $TOKEN"
```
The node joins the cluster automatically. For Kubernetes clusters, it is registered as a worker node. For Swarm clusters, it joins as a Swarm worker.
Detaching removes Hanzo's handle on the cluster; it does not destroy a BYO cluster you brought in.
## Remove Node
## Related
`DELETE /api/cluster.removeNode`
```bash
curl -X DELETE https://platform.hanzo.ai/api/cluster.removeNode \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clusterId": "cluster_abc123",
"nodeId": "node_002"
}'
```
<Callout type="warn">
Workloads on the removed node are automatically rescheduled to remaining nodes. Ensure sufficient capacity before removing nodes.
</Callout>
## Delete Cluster
`DELETE /api/cluster.remove`
```bash
curl -X DELETE https://platform.hanzo.ai/api/cluster.remove \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clusterId": "cluster_abc123"
}'
```
**Response:** `204 No Content`
Destroys all nodes and associated resources. Requires **Manage** cluster permission.
## Cluster Status Values
| Status | Description |
|--------|-------------|
| `provisioning` | Nodes are being created |
| `running` | Cluster is healthy and accepting workloads |
| `degraded` | One or more nodes are unhealthy |
| `updating` | Cluster is being reconfigured |
| `deleting` | Cluster is being destroyed |
| `error` | Provisioning or operation failed |
- [Machines API](/docs/services/platform/api/vms) — individual compute units and GPUs
- [Containers API](/docs/services/platform/api/containers) — the workloads that run on these clusters
@@ -1,96 +1,72 @@
---
title: Containers API
description: Deploy, redeploy, and operate container workloads
description: Deploy, redeploy, and operate container workloads through api.hanzo.ai/v1/platform.
---
A **container** is a deployable workload on Hanzo PaaS. There are two ways to act on one:
A **container** is a deployable workload on Hanzo PaaS — an *app* inside a *project*. Everything you do to one goes through the same host, the same bearer, and the same path shape:
- the canonical **`/v1` container surface** — a machine-driven, zero-downtime **redeploy** (rolling restart) of a running workload, authenticated with a service token;
- the **`/api` build pipeline** — the Dokploy-derived tRPC procedures (`application.*`) that register sources and run builds, authenticated with an API key.
```
https://api.hanzo.ai/v1/platform/projects/{project}/apps/{app}[/action]
```
Under the hood a container maps to an `application` row whose `appName` is the live workload's name.
Get a bearer first — see [Authentication](/docs/services/platform/api/authentication):
## Redeploy a running container (`/v1`)
```bash
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)
```
## Create an app
```bash
curl -fsS -X POST https://api.hanzo.ai/v1/platform/projects/$PROJECT/apps \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "web", "description": "Public web app" }'
```
## Deploy
```bash
curl -fsS -X POST \
"https://platform.hanzo.ai/v1/org/$ORG_ID/project/$PROJECT_ID/env/$ENV_ID/container/$CONTAINER_ID/redeploy" \
-H "Authorization: Bearer $PAAS_SERVICE_TOKEN"
"https://api.hanzo.ai/v1/platform/projects/$PROJECT/apps/web/deploy" \
-H "Authorization: Bearer $TOKEN"
```
```json
{ "ok": true }
```
This re-pulls the image and recreates pods with a rolling update — no new build. The service token comes from [Hanzo KMS](/docs/services/kms).
## Build pipeline procedures (`/api`)
Each procedure is an HTTP `POST` to `https://platform.hanzo.ai/api/<procedure>` with `x-api-key`.
| Procedure | Purpose |
|-----------|---------|
| `application.create` | Register a new application (does not deploy) |
| `application.one` | Get application details |
| `application.all` | List applications in a project |
| `application.update` | Update configuration |
| `application.deploy` | Build & deploy (manual deployment) |
| `application.redeploy` | Rebuild an existing application |
| `application.start` · `application.stop` · `application.reload` | Lifecycle operations |
| `application.remove` | Delete the application |
### Create an application
`name` and `environmentId` are required; `appName` is generated if omitted.
This rolls the workload forward with a zero-downtime rolling update — it re-pulls the image and recreates pods. It does **not** build. To produce a new image first, enqueue a build:
```bash
curl -fsS -X POST https://platform.hanzo.ai/api/application.create \
-H "x-api-key: $PLATFORM_API_KEY" \
curl -fsS -X POST https://api.hanzo.ai/v1/runner \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "web",
"environmentId": "'$ENV_ID'",
"description": "Public web app"
"repo": "https://github.com/hanzoai/docs",
"sha": "'$SHA'",
"image": "ghcr.io/hanzoai/docs:v1.4.0"
}'
# → 202 {"buildJobId":"…","status":"queued","runnerPool":"…","image":"…","target":"…"}
```
Attach a Docker image source with `application.saveDockerProvider` (or a Git source with `application.saveGithubProvider`), then deploy.
Builds run on Hanzo's own in-cluster BuildKit. `repo` must be a full `https://` URL, and `image` must push to a registry we own (`ghcr.io/{hanzoai,luxfi,zooai}/*`) in the caller's own org namespace — the server refuses anything else. Never build images locally.
### Deploy & redeploy
## Lifecycle
```bash
# Build and deploy
curl -fsS -X POST https://platform.hanzo.ai/api/application.deploy \
-H "x-api-key: $PLATFORM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "applicationId": "'$APP_ID'" }'
# Rebuild an existing application
curl -fsS -X POST https://platform.hanzo.ai/api/application.redeploy \
-H "x-api-key: $PLATFORM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "applicationId": "'$APP_ID'", "title": "ship v1.3.0" }'
curl -fsS -X POST ".../apps/web/stop" -H "Authorization: Bearer $TOKEN"
curl -fsS -X POST ".../apps/web/start" -H "Authorization: Bearer $TOKEN"
```
`application.deploy` and `application.redeploy` enqueue a build job and return after it is scheduled. Deployments roll out with a zero-downtime strategy by default.
## Inspect
### Lifecycle
```bash
# Stop / start / restart-in-place
curl -fsS -X POST https://platform.hanzo.ai/api/application.stop -H "x-api-key: $PLATFORM_API_KEY" -d '{"applicationId":"'$APP_ID'"}'
curl -fsS -X POST https://platform.hanzo.ai/api/application.start -H "x-api-key: $PLATFORM_API_KEY" -d '{"applicationId":"'$APP_ID'"}'
curl -fsS -X POST https://platform.hanzo.ai/api/application.reload -H "x-api-key: $PLATFORM_API_KEY" -d '{"applicationId":"'$APP_ID'","appName":"'$APP_NAME'"}'
```
## Inspect & observe
Use the [observe surface](/docs/services/platform) to see declared vs running vs latest image tags and drift:
```bash
curl -fsS "https://platform.hanzo.ai/v1/apps?org=hanzo" \
-H "Authorization: Bearer $PLATFORM_SERVICE_TOKEN"
```
| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/v1/platform/projects/{project}/apps` | List apps |
| `GET` | `/v1/platform/projects/{project}/apps/{app}` | One app |
| `GET` | `/v1/platform/projects/{project}/apps/{app}/deployments` | Deployment history |
| `GET` | `/v1/platform/projects/{project}/apps/{app}/deployments/{id}` | One deployment |
| `GET` | `/v1/platform/projects/{project}/apps/{app}/deployments/{id}/logs` | Deployment logs |
| `DELETE` | `/v1/platform/projects/{project}/apps/{app}` | Remove the app |
## Application status values
@@ -102,5 +78,5 @@ curl -fsS "https://platform.hanzo.ai/v1/apps?org=hanzo" \
| `error` | A deployment or replica failed |
<Callout type="warn">
`application.remove` stops all instances and removes associated domains and volumes. This cannot be undone.
`DELETE` stops all instances and removes associated domains and volumes. This cannot be undone.
</Callout>
@@ -1,120 +1,85 @@
---
title: API Reference
description: Platform REST and tRPC API for managing organizations, clusters, containers, and VMs
description: The one Hanzo PaaS API surface — api.hanzo.ai/v1/platform for projects and apps, api.hanzo.ai/v1/runner for builds.
---
Hanzo Platform exposes two API surfaces: **tRPC** for the dashboard and **REST** for external integrations. Both share the same authentication and authorization layer.
Hanzo PaaS has **one** API surface, on the **one** Hanzo API host.
## Base URLs
## Base URL
| Surface | Base | Auth | Use |
|---------|------|------|-----|
| **Canonical control** | `https://platform.hanzo.ai/v1` | `Authorization: Bearer <service-token>` | Container redeploy, observe/drift (machine) |
| **Build pipeline** | `https://platform.hanzo.ai/api` | `x-api-key: <key>` | Build/deploy applications via tRPC-over-OpenAPI |
| Surface | Base | Auth |
|---------|------|------|
| Control plane | `https://api.hanzo.ai/v1/platform` | `Authorization: Bearer <iam-jwt>` |
| Builds | `https://api.hanzo.ai/v1/runner` | `Authorization: Bearer <iam-jwt>` (org admin) or the fabric build token |
The `/v1` surface is the canonical Hanzo control plane. The `/api` surface is the Dokploy-derived build pipeline, where each endpoint is a tRPC procedure exposed over HTTP as `<router>.<procedure>` (e.g. `application.create`, `domain.create`).
`platform.hanzo.ai` is the **Studio** — the browser UI. It is not an API host: `platform.hanzo.ai/v1/*` answers `500 Service token is not configured on the server`, and `platform.hanzo.ai/api/*` answers the Studio's HTML shell with a `404`. There is no `x-api-key` header, no tRPC procedure surface, and no `/api` prefix anywhere in Hanzo.
## Authentication
The build-pipeline surface accepts an API key (machine) or an IAM bearer token (interactive):
One bearer, minted from a machine identity in [Hanzo KMS](/docs/services/kms) or from an interactive [Hanzo IAM](/docs/services/iam) login:
```bash
curl https://platform.hanzo.ai/api/organization.all \
-H "x-api-key: $PLATFORM_API_KEY"
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)
curl -fsS https://api.hanzo.ai/v1/platform/projects \
-H "Authorization: Bearer $TOKEN"
```
The canonical `/v1` surface uses a service token: `Authorization: Bearer $PAAS_SERVICE_TOKEN`. Both credential types come from [Hanzo KMS](/docs/services/kms). See [Authentication](/docs/services/platform/api/authentication) for details.
See [Authentication](/docs/services/platform/api/authentication).
## tRPC Procedures
## Endpoints
The build pipeline exposes these tRPC routers:
| Method | Path | Purpose |
|--------|------|---------|
| `GET` · `POST` | `/v1/platform/projects` | List / create projects |
| `GET` · `DELETE` | `/v1/platform/projects/{project}` | Inspect / delete a project |
| `GET` · `POST` | `/v1/platform/projects/{project}/apps` | List / create apps |
| `GET` · `DELETE` | `/v1/platform/projects/{project}/apps/{app}` | Inspect / delete an app |
| `POST` | `/v1/platform/projects/{project}/apps/{app}/deploy` | Roll the app forward |
| `POST` | `/v1/platform/projects/{project}/apps/{app}/start` | Start the workload |
| `POST` | `/v1/platform/projects/{project}/apps/{app}/stop` | Stop the workload |
| `GET` | `/v1/platform/projects/{project}/apps/{app}/deployments` | Deployment history |
| `GET` | `/v1/platform/projects/{project}/apps/{app}/deployments/{id}` | One deployment |
| `GET` | `/v1/platform/projects/{project}/apps/{app}/deployments/{id}/logs` | Deployment logs |
| `GET` · `POST` | `/v1/platform/sites` | Static sites |
| `GET` | `/v1/platform/health` | Liveness |
| `POST` | `/v1/runner` | Enqueue an in-cluster image build |
| Router | Description |
|--------|-------------|
| `organization` | Organization CRUD |
| `orgTeam` | Team member management |
| `cluster` | Cluster lifecycle |
| `application` | Application deployments (the unit a container maps to) |
| `compose` | Multi-service compose/stack units |
| `project` | Project management |
| `environment` | Environment configuration |
| `build` | Build pipelines |
| `log` | Log streaming |
| `registry` | Container registry |
| `domain` | Domain and DNS management |
| `git` | Git integration |
| `user` | User profile |
| `provisioner` | Infrastructure provisioning |
| `audit` | Audit log |
| `invitation` | Team invitations |
| `system` | System health and status |
## Request format
## Request Format
tRPC requests use HTTP POST with a JSON body:
JSON in, JSON out. Requests are plain REST — no envelope, no procedure names:
```bash
curl -X POST https://platform.hanzo.ai/api/cluster.create \
-H "Authorization: Bearer YOUR_TOKEN" \
curl -fsS -X POST https://api.hanzo.ai/v1/platform/projects \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "production",
"provider": "digitalocean",
"region": "sfo3"
}'
-d '{ "slug": "web", "name": "Web" }'
```
## Response Format
## Error responses
All responses return JSON:
Errors are a flat JSON object with the HTTP status echoed in the body:
```json
{
"result": {
"data": {
"id": "cluster_abc123",
"name": "production",
"status": "provisioning"
}
}
}
{ "status": 403, "error": "X-Org-Id required" }
```
## Error Responses
| Status | Meaning |
|--------|---------|
| `400` | Invalid input |
| `401` | Missing or invalid bearer |
| `403` | Validated principal lacks the scope (or no org context) |
| `404` | Resource does not exist |
| `409` | Conflict (stale version, duplicate slug) |
| `500` | Server error |
Errors return an appropriate HTTP status code with a JSON body:
## Rate limits
```json
{
"error": {
"message": "Cluster not found",
"code": "NOT_FOUND",
"httpStatus": 404
}
}
```
| Code | Meaning |
|------|---------|
| `BAD_REQUEST` | Invalid input |
| `UNAUTHORIZED` | Missing or invalid token |
| `FORBIDDEN` | Insufficient permissions |
| `NOT_FOUND` | Resource does not exist |
| `CONFLICT` | Resource already exists |
| `INTERNAL_SERVER_ERROR` | Server error |
## Rate Limits
| Endpoint Type | Rate Limit |
|--------------|------------|
| Read operations | 100 requests/minute |
| Write operations | 30 requests/minute |
| Log streaming | 10 concurrent connections |
Rate limit headers are included in every response:
Rate-limit headers ride every response:
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1709000000
```
@@ -1,205 +1,40 @@
---
title: Organizations API
description: Create, read, update, and delete organizations
description: Organizations are an identity concept — they live in Hanzo IAM at api.hanzo.ai/v1/iam.
---
The Organizations API manages the top-level entity in Hanzo Platform. Every cluster, project, and team member belongs to an organization.
An organization is the top-level tenant: every cluster, project, machine, secret, and team member belongs to one. It is an **identity** object, so it lives in [Hanzo IAM](/docs/services/iam) — not in a second directory owned by the deploy plane.
All calls take an IAM bearer — see [Authentication](/docs/services/platform/api/authentication).
## Endpoints
| Procedure | Method | Description |
|-----------|--------|-------------|
| `organization.all` | GET | List all organizations for the authenticated user |
| `organization.one` | GET | Get a single organization by ID |
| `organization.create` | POST | Create a new organization |
| `organization.update` | PATCH | Update organization settings |
| `organization.remove` | DELETE | Delete an organization |
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/iam/get-organizations?owner=admin` | List organizations visible to the caller |
| `GET` | `/v1/iam/get-organization?id=<owner>/<name>` | One organization |
| `POST` | `/v1/iam/add-organization` | Create an organization |
| `POST` | `/v1/iam/update-organization?id=<owner>/<name>` | Update an organization |
| `POST` | `/v1/iam/delete-organization` | Delete an organization |
## List Organizations
`GET /api/organization.all`
Returns all organizations the authenticated user is a member of.
These are administrative endpoints: a normal member bearer gets `403 forbidden`. Org membership and roles for ordinary users are managed in the [Console](https://console.hanzo.ai).
```bash
curl https://platform.hanzo.ai/api/organization.all \
-H "Authorization: Bearer YOUR_TOKEN"
curl -fsS "https://api.hanzo.ai/v1/iam/get-organizations?owner=admin" \
-H "Authorization: Bearer $TOKEN"
```
**Response:**
## How the org reaches the rest of the API
You never pass an org id by hand. The gateway strips every client-supplied identity header and re-mints `X-Org-Id` from the validated JWT's `owner` claim, so each request is scoped to exactly the org the token belongs to. A call with no org context answers:
```json
{
"result": {
"data": [
{
"id": "org_abc123",
"name": "Acme Corp",
"description": "Production infrastructure",
"createdAt": "2025-01-15T10:00:00Z",
"memberCount": 12,
"clusterCount": 3
},
{
"id": "org_def456",
"name": "Staging",
"description": "Development and testing",
"createdAt": "2025-03-20T14:30:00Z",
"memberCount": 5,
"clusterCount": 1
}
]
}
}
{ "status": 403, "error": "X-Org-Id required" }
```
## Get Organization
That is the signal to re-authenticate, not to add a header.
`GET /api/organization.one?organizationId=org_abc123`
## Related
```bash
curl "https://platform.hanzo.ai/api/organization.one?organizationId=org_abc123" \
-H "Authorization: Bearer YOUR_TOKEN"
```
**Response:**
```json
{
"result": {
"data": {
"id": "org_abc123",
"name": "Acme Corp",
"description": "Production infrastructure",
"createdAt": "2025-01-15T10:00:00Z",
"memberCount": 12,
"clusterCount": 3,
"members": [
{
"userId": "user_abc",
"email": "alice@acme.com",
"role": "owner",
"joinedAt": "2025-01-15T10:00:00Z"
}
]
}
}
}
```
## Create Organization
`POST /api/organization.create`
```bash
curl -X POST https://platform.hanzo.ai/api/organization.create \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"description": "Production infrastructure"
}'
```
**Parameters:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Organization name (2-50 characters) |
| `description` | string | No | Short description |
**Response:** `201 Created`
```json
{
"result": {
"data": {
"id": "org_new789",
"name": "Acme Corp",
"description": "Production infrastructure",
"createdAt": "2026-02-26T10:00:00Z"
}
}
}
```
The authenticated user is automatically assigned the **Owner** role.
## Update Organization
`PATCH /api/organization.update`
```bash
curl -X PATCH https://platform.hanzo.ai/api/organization.update \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_abc123",
"name": "Acme Corporation",
"description": "Updated description"
}'
```
**Parameters:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `organizationId` | string | Yes | Organization ID |
| `name` | string | No | New name |
| `description` | string | No | New description |
Requires **Owner** or **Admin** role.
## Delete Organization
`DELETE /api/organization.remove`
```bash
curl -X DELETE https://platform.hanzo.ai/api/organization.remove \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_abc123"
}'
```
**Response:** `204 No Content`
<Callout type="warn">
Deleting an organization permanently removes all clusters, projects, environments, and data. This action cannot be undone. Requires the **Owner** role.
</Callout>
## Team Management
Team members are managed via the `orgTeam` router:
| Procedure | Method | Description |
|-----------|--------|-------------|
| `orgTeam.all` | GET | List all members of an organization |
| `orgTeam.update` | PATCH | Change a member's role |
| `orgTeam.remove` | DELETE | Remove a member |
```bash
# List members
curl "https://platform.hanzo.ai/api/orgTeam.all?organizationId=org_abc123" \
-H "Authorization: Bearer YOUR_TOKEN"
# Change role
curl -X PATCH https://platform.hanzo.ai/api/orgTeam.update \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_abc123",
"userId": "user_def456",
"role": "admin"
}'
# Remove member
curl -X DELETE https://platform.hanzo.ai/api/orgTeam.remove \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_abc123",
"userId": "user_def456"
}'
```
- [Hanzo IAM](/docs/services/iam) — identity, SSO, and org membership
- [Authentication](/docs/authentication) — the one OIDC integration path
@@ -1,277 +1,83 @@
---
title: Virtual Machines API
description: Launch, manage, and destroy Visor VMs via the API
title: Machines API
description: Launch, inspect, and destroy machines and GPUs through api.hanzo.ai/v1.
---
The Virtual Machines API manages Visor VMs -- full Linux virtual machines with root access, persistent storage, and SSH connectivity.
A **machine** is a compute unit — a Visor-provisioned VM, or a bring-your-own worker that dialed in with `hanzo link`. Both appear in the same list, distinguished by `provider`.
All calls take an IAM bearer — see [Authentication](/docs/services/platform/api/authentication).
## Endpoints
| Procedure | Method | Description |
|-----------|--------|-------------|
| `vm.all` | GET | List all VMs |
| `vm.one` | GET | Get VM details |
| `vm.launch` | POST | Launch a new VM |
| `vm.start` | POST | Start a stopped VM |
| `vm.stop` | POST | Stop a running VM |
| `vm.restart` | POST | Restart a VM |
| `vm.destroy` | DELETE | Permanently destroy a VM |
| `vm.volumes` | GET | List attached volumes |
| `vm.attachVolume` | POST | Attach a storage volume |
| `vm.detachVolume` | POST | Detach a storage volume |
| `vm.terminal` | GET | Open a web terminal session |
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/machines` | List every machine the org can see |
| `POST` | `/v1/machines` | Launch a machine |
| `GET` | `/v1/machines/{id}` | Machine detail |
| `DELETE` | `/v1/machines/{id}` | Destroy a machine |
| `GET` | `/v1/gpus` | Every GPU across the org's compute |
| `GET` | `/v1/gpus/alerts` | GPU health alerts |
| `GET` | `/v1/fleet` | The unified board — every compute source with its latest utilization |
| `GET` | `/v1/fleet/workers` | BYO workers only |
| `GET` | `/v1/fleet/jobs` | The org's GPU job queue |
| `POST` | `/v1/fleet/jobs/{id}/cancel` | Cancel a queued or running job |
| `GET` · `POST` | `/v1/fleet/samples` | Read back / self-report utilization samples |
## List VMs
`GET /api/vm.all?organizationId=org_abc123`
## List machines
```bash
curl "https://platform.hanzo.ai/api/vm.all?organizationId=org_abc123" \
-H "Authorization: Bearer YOUR_TOKEN"
curl -fsS https://api.hanzo.ai/v1/machines \
-H "Authorization: Bearer $TOKEN"
```
**Response:**
```json
{
"result": {
"data": [
{
"id": "vm_abc123",
"name": "ml-training",
"plan": "power",
"region": "us-east",
"os": "ubuntu-22.04",
"ip": "203.0.113.50",
"status": "running",
"createdAt": "2026-01-10T10:00:00Z"
}
]
}
}
```
## Get VM
`GET /api/vm.one?vmId=vm_abc123`
```bash
curl "https://platform.hanzo.ai/api/vm.one?vmId=vm_abc123" \
-H "Authorization: Bearer YOUR_TOKEN"
```
**Response:**
```json
{
"result": {
"data": {
"id": "vm_abc123",
"name": "ml-training",
"plan": "power",
"region": "us-east",
"os": "ubuntu-22.04",
"ip": "203.0.113.50",
"privateIp": "10.0.1.50",
"status": "running",
"specs": {
"vcpu": 8,
"memoryGb": 16,
"diskGb": 320,
"diskType": "ssd"
},
"sshKeys": ["ssh-ed25519 AAAA..."],
"volumes": [
{
"id": "vol_xyz789",
"name": "training-data",
"sizeGb": 500,
"mountPath": "/mnt/data"
}
],
"createdAt": "2026-01-10T10:00:00Z"
"machines": [
{
"id": "dbc",
"name": "dbc",
"region": "on-prem",
"type": "byo-gpu",
"status": "offline",
"provider": "byo"
}
}
]
}
```
## Launch VM
`POST /api/vm.launch`
## List GPUs
```bash
curl -X POST https://platform.hanzo.ai/api/vm.launch \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_abc123",
"name": "ml-training",
"plan": "power",
"region": "us-east",
"os": "ubuntu-22.04",
"sshKeys": ["ssh-ed25519 AAAA..."],
"userData": "#!/bin/bash\napt-get update && apt-get install -y docker.io"
}'
curl -fsS https://api.hanzo.ai/v1/gpus \
-H "Authorization: Bearer $TOKEN"
```
**Parameters:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `organizationId` | string | Yes | Organization ID |
| `name` | string | Yes | VM name |
| `plan` | string | Yes | VM plan (nano, micro, small, medium, power, power-dedicated) |
| `region` | string | Yes | Region code (us-east, us-west, eu-west, ap-south) |
| `os` | string | Yes | OS image |
| `sshKeys` | array | No | SSH public keys for root access |
| `userData` | string | No | Cloud-init script (runs on first boot) |
**Available OS images:**
| Image | Description |
|-------|-------------|
| `ubuntu-22.04` | Ubuntu 22.04 LTS |
| `ubuntu-24.04` | Ubuntu 24.04 LTS |
| `debian-12` | Debian 12 Bookworm |
| `rocky-9` | Rocky Linux 9 |
| `fedora-40` | Fedora 40 |
**Response:** `201 Created`
```json
{
"result": {
"data": {
"id": "vm_new456",
"name": "ml-training",
"status": "provisioning",
"ip": null,
"createdAt": "2026-02-26T10:00:00Z"
"gpus": [
{
"id": "dbc#0",
"name": "dbc",
"model": "Apple M4 Max (Metal)",
"region": "on-prem",
"status": "offline"
}
}
]
}
```
The VM enters `provisioning` status. It transitions to `running` with an assigned IP within 30-90 seconds.
## Stop VM
`POST /api/vm.stop`
Gracefully shuts down the VM. The VM retains its IP address and storage. You are not billed for compute while stopped, but storage charges continue.
## Destroy a machine
```bash
curl -X POST https://platform.hanzo.ai/api/vm.stop \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "vmId": "vm_abc123" }'
```
## Start VM
`POST /api/vm.start`
Boots a stopped VM. The VM retains its previous IP and all attached volumes.
```bash
curl -X POST https://platform.hanzo.ai/api/vm.start \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "vmId": "vm_abc123" }'
```
## Restart VM
`POST /api/vm.restart`
Performs a graceful reboot.
```bash
curl -X POST https://platform.hanzo.ai/api/vm.restart \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "vmId": "vm_abc123" }'
```
## Destroy VM
`DELETE /api/vm.destroy`
Permanently destroys the VM and its boot disk. Attached volumes are detached but not deleted.
```bash
curl -X DELETE https://platform.hanzo.ai/api/vm.destroy \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "vmId": "vm_abc123" }'
```
**Response:** `204 No Content`
<Callout type="warn">
Destroying a VM deletes its boot disk permanently. Attached volumes are preserved. Back up any data on the boot disk before destroying.
</Callout>
## Volume Management
### List Volumes
```bash
curl "https://platform.hanzo.ai/api/vm.volumes?vmId=vm_abc123" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Attach Volume
```bash
curl -X POST https://platform.hanzo.ai/api/vm.attachVolume \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"vmId": "vm_abc123",
"volumeId": "vol_xyz789",
"mountPath": "/mnt/data"
}'
```
The volume is attached as a block device. If `mountPath` is provided, the platform mounts it automatically (requires the Hanzo agent on the VM).
### Detach Volume
```bash
curl -X POST https://platform.hanzo.ai/api/vm.detachVolume \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"vmId": "vm_abc123",
"volumeId": "vol_xyz789"
}'
curl -fsS -X DELETE "https://api.hanzo.ai/v1/machines/$MACHINE_ID" \
-H "Authorization: Bearer $TOKEN"
```
<Callout type="warn">
Always unmount the filesystem inside the VM before detaching a volume to prevent data corruption.
Destroying a machine is permanent. Detach or snapshot any volume you still need first.
</Callout>
## Web Terminal
## Related
`GET /api/vm.terminal?vmId=vm_abc123`
Opens a WebSocket-based terminal session to the VM. Used by the dashboard for browser-based SSH.
```bash
# The dashboard uses this endpoint internally.
# For direct SSH access, use the VM's public IP:
ssh root@203.0.113.50
```
## VM Status Values
| Status | Description |
|--------|-------------|
| `provisioning` | VM is being created |
| `running` | VM is up and accepting connections |
| `stopping` | Graceful shutdown in progress |
| `stopped` | VM is off (storage preserved) |
| `restarting` | Reboot in progress |
| `destroying` | VM is being deleted |
| `error` | Provisioning or operation failed |
- [Clusters API](/docs/services/platform/api/clusters) — Kubernetes clusters and node pools
- [Containers API](/docs/services/platform/api/containers) — the workloads that run on this compute
@@ -1,22 +1,26 @@
---
title: Hanzo PaaS
description: The platform.hanzo.ai control plane — deploy and operate applications, containers, and custom domains across your clusters, with IAM-native multi-org access.
description: Deploy and operate applications, containers, and custom domains across your clusters — Studio at platform.hanzo.ai, API at api.hanzo.ai/v1/platform, with IAM-native multi-org access.
---
# Hanzo PaaS
> **API reference** · [Hanzo Platform API →](/docs/openapi/platform) — every endpoint, generated from the OpenAPI spec.
Hanzo PaaS (`platform.hanzo.ai`) is the control plane for shipping software on Hanzo infrastructure. You register an application, point it at a Docker image or Git repo, attach a domain, and the platform builds, deploys, and operates it on your clusters — with deployment history, rolling updates, and drift detection. Authentication is delegated to [Hanzo IAM](/docs/services/iam) with per-organization isolation.
Hanzo PaaS is the control plane for shipping software on Hanzo infrastructure — Studio at `platform.hanzo.ai`, API at `api.hanzo.ai/v1/platform`. You register an application, point it at a Docker image or Git repo, attach a domain, and the platform builds, deploys, and operates it on your clusters — with deployment history, rolling updates, and drift detection. Authentication is delegated to [Hanzo IAM](/docs/services/iam) with per-organization isolation.
| | |
|---|---|
| **Studio** | `https://platform.hanzo.ai` |
| **Canonical API** | `https://platform.hanzo.ai/v1` |
| **Build pipeline API** | `https://platform.hanzo.ai/api` (tRPC + OpenAPI) |
| **Auth** | Hanzo IAM (interactive) · API key / service token (machine) |
| **Studio** (browser UI) | `https://platform.hanzo.ai` |
| **API** | `https://api.hanzo.ai/v1/platform` — the one Hanzo API host |
| **Build** | `POST https://api.hanzo.ai/v1/runner` — the one build door |
| **Auth** | Hanzo IAM bearer (interactive **and** machine); build token for fabric automation |
| **Source** | [github.com/hanzoai/platform](https://github.com/hanzoai/platform) |
<Callout>
`platform.hanzo.ai` is the **Studio** — a browser UI, not an API host. Every programmatic call goes to `api.hanzo.ai/v1/platform/...`, the same host and the same key as AI, KMS, and analytics.
</Callout>
## The model
Hanzo PaaS organizes work into a simple hierarchy:
@@ -45,9 +49,10 @@ Two credential types, both retrieved from [Hanzo KMS](/docs/services/kms) — ne
| Credential | Header | Use |
|------------|--------|-----|
| **API key** | `x-api-key: <key>` | Machine access to the build-pipeline API (`/api/*`). Org-scoped; issued under **Settings → API Keys** |
| **Service token** | `Authorization: Bearer <token>` | The canonical `/v1` container surface (redeploy, observe) |
| **IAM token** | `Authorization: Bearer <iam-jwt>` | Interactive / user access, via [Hanzo IAM](/docs/services/iam) SSO |
| **IAM token** | `Authorization: Bearer <iam-jwt>` | Everything. One login authorizes reads, deploys, and builds. Mint it with the machine-identity exchange below, or via [Hanzo IAM](/docs/services/iam) SSO. |
| **Build token** | `Authorization: Bearer <build-token>` | Fabric automation only (git-push-to-deploy, self-release). Never held by a user. |
There is no `x-api-key` header and no second credential type — one bearer, one host.
## Deploy
@@ -58,96 +63,75 @@ Two credential types, both retrieved from [Hanzo KMS](/docs/services/kms) — ne
3. Add an application — choose a Docker image or connect a Git repository.
4. Attach a domain and deploy.
### Redeploy a running container (canonical `/v1`)
### Get a bearer
The canonical, machine-driven redeploy performs a zero-downtime rolling restart of the live workload — it re-pulls the image and recreates pods. It takes a service token and no body:
The machine identity comes from [Hanzo KMS](/docs/services/kms); the exchange is the same one every Hanzo service uses.
```bash
# Service token comes from KMS — never inline a real value
TOKEN=$(curl -sS -X POST https://kms.hanzo.ai/v1/kms/auth/login \
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)
PAAS_TOKEN=$(curl -sS "https://kms.hanzo.ai/v1/kms/orgs/hanzo/secrets/paas/SERVICE_TOKEN?env=production" \
-H "Authorization: Bearer $TOKEN" | jq -r .secret.value)
```
### Projects and apps
```bash
# List projects in the caller's org
curl -fsS https://api.hanzo.ai/v1/platform/projects \
-H "Authorization: Bearer $TOKEN"
# Apps in a project
curl -fsS https://api.hanzo.ai/v1/platform/projects/$PROJECT/apps \
-H "Authorization: Bearer $TOKEN"
```
| Method | Path | Purpose |
|--------|------|---------|
| `GET` · `POST` | `/v1/platform/projects` | List / create projects |
| `GET` · `DELETE` | `/v1/platform/projects/{project}` | Inspect / delete a project |
| `GET` · `POST` | `/v1/platform/projects/{project}/apps` | List / create apps |
| `GET` · `DELETE` | `/v1/platform/projects/{project}/apps/{app}` | Inspect / delete an app |
| `POST` | `/v1/platform/projects/{project}/apps/{app}/deploy` | Roll the app forward |
| `POST` | `/v1/platform/projects/{project}/apps/{app}/start` · `/stop` | Manage the running workload |
| `GET` | `/v1/platform/projects/{project}/apps/{app}/deployments[/{id}[/logs]]` | Deployment history and logs |
| `GET` · `POST` | `/v1/platform/sites` | Static sites |
| `GET` | `/v1/platform/health` | Liveness — `{"k8s":true,"service":"platform","status":"ok"}` |
### Redeploy
```bash
curl -fsS -X POST \
"https://platform.hanzo.ai/v1/org/$ORG_ID/project/$PROJECT_ID/env/$ENV_ID/container/$APP_ID/redeploy" \
-H "Authorization: Bearer $PAAS_TOKEN"
# → {"ok":true}
"https://api.hanzo.ai/v1/platform/projects/$PROJECT/apps/$APP/deploy" \
-H "Authorization: Bearer $TOKEN"
```
### Build and deploy (pipeline `/api`)
### Build an image
To register a new app and run a build through the deployment pipeline, use the OpenAPI surface with an API key. Procedures are `<router>.<procedure>`:
Builds run in-cluster on Hanzo's own BuildKit — never on a laptop, never on a third-party runner. One door:
```bash
# 1. Register the application (does not deploy)
curl -fsS -X POST https://platform.hanzo.ai/api/application.create \
-H "x-api-key: $PLATFORM_API_KEY" \
curl -fsS -X POST https://api.hanzo.ai/v1/runner \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "web", "environmentId": "'$ENV_ID'" }'
# 2. Attach a Docker image source, then deploy
curl -fsS -X POST https://platform.hanzo.ai/api/application.deploy \
-H "x-api-key: $PLATFORM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "applicationId": "'$APP_ID'" }'
# Rebuild an existing application
curl -fsS -X POST https://platform.hanzo.ai/api/application.redeploy \
-H "x-api-key: $PLATFORM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "applicationId": "'$APP_ID'", "title": "ship v1.3.0" }'
-d '{
"repo": "https://github.com/hanzoai/docs",
"sha": "'$SHA'",
"image": "ghcr.io/hanzoai/docs:v1.4.0"
}'
# → 202 {"buildJobId":"…","status":"queued","runnerPool":"…","image":"…","target":"…"}
```
`application.deploy` and `application.redeploy` enqueue a build job; `application.start`, `application.stop`, and `application.reload` manage the running workload. See the [Containers API](/docs/services/platform/api/containers) for the full procedure set.
`repo` must be a full `https://` URL; `image` must push to a registry we own (`ghcr.io/{hanzoai,luxfi,zooai}/*`) whose namespace matches the caller's org. `sha` pins the commit — `ref` or `branch` are accepted, defaulting to `main`.
## Observe & drift
The platform continuously reconciles three views of every app and computes drift:
| Tag | Meaning |
|-----|---------|
| `declared` | The version your operator/manifest declares |
| `running` | The image actually live in the cluster |
| `latest` | The newest released version available |
```bash
# List apps with drift, scoped to an org
curl -fsS "https://platform.hanzo.ai/v1/apps?org=hanzo&drift=true" \
-H "Authorization: Bearer $PLATFORM_SERVICE_TOKEN"
```
```json
{
"apps": [
{ "org": "hanzo", "app": "docs", "env": "production",
"declared_tag": "v1.4.0", "running_tag": "v1.3.0", "latest_tag": "v1.4.0",
"health": "yellow", "drift": ["un-rolled"] }
],
"summary": { "total": 1, "byDrift": { "un-rolled": 1 } }
}
```
Drift is never stored — it is recomputed from declared/running/latest each time. `POST /v1/apps/sync` refreshes the observed columns from the cluster and release feeds.
<Callout type="warn">
Do not post to `platform.hanzo.ai/v1/*` or `platform.hanzo.ai/api/*`. The first returns `500 Service token is not configured on the server`; the second returns the Studio's HTML shell with a `404`. Neither is an API.
</Callout>
## Custom domains & TLS
Attach a hostname to an application; TLS is provisioned automatically (Let's Encrypt) and a DNS record is created:
```bash
curl -fsS -X POST https://platform.hanzo.ai/api/domain.create \
-H "x-api-key: $PLATFORM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"applicationId": "'$APP_ID'",
"host": "app.example.com",
"https": true,
"certificateType": "letsencrypt"
}'
```
Point your DNS at the platform ingress IP shown on the project's **Domains** tab.
Attach it in the Studio under the project's **Domains** tab, then point your DNS at the ingress IP shown there. Records themselves are managed through [Hanzo DNS](/docs/services/dns) at `api.hanzo.ai/v1/dns`.
## Self-hosting
@@ -164,10 +148,10 @@ The platform runs as a Next.js application with an embedded SQLite database; for
<Cards>
<Card title="API Overview" href="/docs/services/platform/api">
The `/v1` control surface and the `/api` build pipeline
The `/v1/platform` control surface and `/v1/runner` builds
</Card>
<Card title="Authentication" href="/docs/services/platform/api/authentication">
API keys, service tokens, and IAM tokens
IAM bearers and the fabric build token
</Card>
<Card title="Containers API" href="/docs/services/platform/api/containers">
Create, deploy, redeploy, and operate applications
@@ -60,7 +60,6 @@ Hanzo Extensions is a **monorepo of browser and IDE extensions** providing AI-po
5. Extracts token, closes tab
```
**LLM endpoint**: `api.hanzo.ai/v1/chat/completions` (NOT `llm.hanzo.ai` which is Cloud UI)
### Auth Implementation
@@ -101,7 +100,7 @@ async function startAuth(): Promise<string> {
```typescript
// /oauth/userinfo only returns `sub` — use /v1/iam/get-account for full profile
async function getProfile(token: string) {
const res = await fetch("https://iam.hanzo.ai/v1/iam/get-account", {
const res = await fetch("https://api.hanzo.ai/v1/iam/get-account", {
headers: { "Authorization": `Bearer ${token}` }
})
return res.json() // { name, email, avatar, ... }
@@ -141,7 +140,6 @@ cd packages/jetbrains
| Issue | Cause | Solution |
|-------|-------|----------|
| Code flow fails | Hanzo IAM empty grant_type bug | Use implicit flow (response_type=token) |
| LLM returns 404 | Using llm.hanzo.ai | Use api.hanzo.ai/v1/chat/completions |
| Safari build fails | Missing Xcode | Build on macOS with Xcode installed |
| JetBrains build fails | Missing gradle wrapper | Commit gradle-wrapper.jar |
+5 -5
View File
@@ -57,7 +57,7 @@ For **client-side OAuth flows** (login UI, token extraction, redirect handling),
| Item | Value |
|------|-------|
| Admin UI | `https://iam.hanzo.ai` (internal) |
| Admin UI | `https://hanzo.id` (internal) |
| Login UI | `https://hanzo.id` (separate repo: hanzoai/id) |
| API port | 8000 |
| LDAP port | 389 (LDAPS: 636) |
@@ -162,11 +162,11 @@ cp conf/app.dev.conf conf/app.conf
```bash
# Get all users in an organization
curl -s "https://iam.hanzo.ai/v1/iam/get-users?owner=hanzo" \
curl -s "https://api.hanzo.ai/v1/iam/get-users?owner=hanzo" \
-H "Authorization: Bearer ${ADMIN_TOKEN}"
# Create a new application
curl -X POST "https://iam.hanzo.ai/v1/iam/add-application" \
curl -X POST "https://api.hanzo.ai/v1/iam/add-application" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
@@ -181,7 +181,7 @@ curl -X POST "https://iam.hanzo.ai/v1/iam/add-application" \
}'
# Update user balance (billing integration)
curl -X POST "https://iam.hanzo.ai/api/update-user" \
curl -X POST "https://api.hanzo.ai/v1/iam/update-user" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
@@ -378,7 +378,7 @@ POSTGRES_PASSWORD=<generate-secure-password>
POSTGRES_DB=iam
# IAM server
IAM_ORIGIN=https://iam.hanzo.ai
IAM_ORIGIN=https://hanzo.id
ENCRYPTION_KEY=<32-byte-hex-key>
ENABLE_MULTI_TENANT=true
ALLOWED_ORIGINS=hanzo.id,zoo.id,lux.id,pars.id,iam.hanzo.ai
+2 -2
View File
@@ -38,7 +38,7 @@ Hanzo ID is the **unified identity platform** for the entire Hanzo/Lux/Zoo ecosy
| Item | Value |
|------|-------|
| Login UI | `https://hanzo.id` |
| IAM API | `https://iam.hanzo.ai` (backend) |
| IAM API | `https://hanzo.id` (backend) |
| Authorize | `/oauth/authorize` |
| Token | `/oauth/token` |
| Userinfo | `/oauth/userinfo` (limited: sub/iss/aud only) |
@@ -89,7 +89,7 @@ const accessToken = hash.get("access_token")
const state = hash.get("state")
// Get full profile (userinfo only returns sub)
const profile = await fetch("https://iam.hanzo.ai/v1/iam/get-account", {
const profile = await fetch("https://api.hanzo.ai/v1/iam/get-account", {
headers: { "Authorization": `Bearer ${accessToken}` }
}).then(r => r.json())
// profile.name, profile.email, profile.avatar, etc.
@@ -171,7 +171,7 @@ spec:
| Domain | Backend Service |
|--------|-----------------|
| `hanzo.ai` | hanzo-app |
| `api.hanzo.ai`, `llm.hanzo.ai` | Hanzo Gateway |
| `api.hanzo.ai` | Hanzo API (one host for every `/v1/<service>`) |
| `hanzo.id`, `lux.id`, `zoo.id` | IAM (Hanzo IAM) |
| `kms.hanzo.ai` | KMS (Hanzo KMS) |
| `platform.hanzo.ai` | Platform (Dokploy) |
@@ -41,7 +41,7 @@ Repo: `github.com/hanzoai/llm`.
| Internal endpoint | `http://llm.hanzo.svc.cluster.local:4000/v1` |
| Port | 4000 |
| Config | `config.yaml` or env vars |
| Dashboard | `https://llm.hanzo.ai` (Cloud UI, NOT LLM endpoint) |
| Dashboard | `https://console.hanzo.ai` |
| Repo | `github.com/hanzoai/llm` |
## One-file quickstart
@@ -46,8 +46,8 @@ Repo: `hanzoai/paas`.
| Item | Value |
|------|-------|
| UI | `https://platform.hanzo.ai` |
| API | `https://platform.hanzo.ai/v1` |
| Studio (UI) | `https://platform.hanzo.ai` |
| API | `https://api.hanzo.ai/v1/platform` (builds: `POST /v1/runner`) |
| Auth | IAM via hanzo.id (OAuth2) |
| Images | `ghcr.io/hanzoai/paas-{api,studio,monitor,sync,webhook}` |
| K8s SA | `hanzo-paas-sa` |
@@ -60,16 +60,19 @@ Repo: `hanzoai/paas`.
```bash
# Create a project
curl -X POST https://platform.hanzo.ai/v1/projects \
curl -X POST https://api.hanzo.ai/v1/platform/projects \
-H "Authorization: Bearer ${HANZO_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "my-ai-app",
"environment": "production",
"image": "ghcr.io/myorg/my-app:latest",
"port": 3000,
"replicas": 2
}'
-d '{ "slug": "my-ai-app", "name": "My AI App" }'
# Add an app to it, then roll it forward
curl -X POST https://api.hanzo.ai/v1/platform/projects/my-ai-app/apps \
-H "Authorization: Bearer ${HANZO_TOKEN}" \
-H "Content-Type: application/json" \
-d '{ "name": "web", "image": "ghcr.io/hanzoai/my-app:v1.0.0", "port": 3000, "replicas": 2 }'
curl -X POST https://api.hanzo.ai/v1/platform/projects/my-ai-app/apps/web/deploy \
-H "Authorization: Bearer ${HANZO_TOKEN}"
```
### Docker Compose (self-hosted)
+1 -1
View File
@@ -117,7 +117,7 @@ I need it to extract titles from a list of URLs.
%tools # List available MCP tools
%tool read_file {"file_path": "README.md"} # Execute specific tool
%model claude-3-opus-20240229 # Change model
%model zen5 # Change model
%edit_self ipython_repl.py # Edit REPL source code
```
+1 -1
View File
@@ -84,7 +84,7 @@ const res = await client.chat.completions.create({
});
// The model that actually served the request:
console.log(res.model); // e.g. "zen-nano" | "zen-omni" | a frontier model
console.log(res.model); // e.g. "zen-nano" | "zen5" | a frontier model
```
</TabsContent>
</Tabs>
@@ -259,7 +259,7 @@ export default function PlatformPage() {
<div className="flex flex-col items-center gap-0.5 text-fd-muted-foreground/40">
<span className="text-[10px] font-mono">AI API</span>
<span className="text-xs">|</span>
<span className="text-[10px] font-mono">llm.hanzo.ai</span>
<span className="text-[10px] font-mono">api.hanzo.ai/v1</span>
</div>
</div>
</section>
@@ -25,12 +25,12 @@ A generation is an LLM call with full input/output capture, token counts, model
## Instrumentation
### Auto-Instrumentation (Hanzo AI API)
If routing through `llm.hanzo.ai`, all calls are automatically traced:
Calls through the Hanzo AI API (`api.hanzo.ai/v1`), all calls are automatically traced:
```bash
curl https://llm.hanzo.ai/v1/chat/completions \
curl https://api.hanzo.ai/v1/chat/completions \
-H "Authorization: Bearer $HANZO_API_KEY" \
-d '{"model": "zen-4o", "messages": [{"role": "user", "content": "Hello"}]}'
-d '{"model": "zen5", "messages": [{"role": "user", "content": "Hello"}]}'
# → Automatically creates trace + generation in Insights
```
+1 -1
View File
@@ -259,7 +259,7 @@ export default function PlatformPage() {
<div className="flex flex-col items-center gap-0.5 text-fd-muted-foreground/40">
<span className="text-[10px] font-mono">AI API</span>
<span className="text-xs">|</span>
<span className="text-[10px] font-mono">llm.hanzo.ai</span>
<span className="text-[10px] font-mono">api.hanzo.ai/v1</span>
</div>
</div>
</section>
@@ -259,7 +259,7 @@ export default function PlatformPage() {
<div className="flex flex-col items-center gap-0.5 text-fd-muted-foreground/40">
<span className="text-[10px] font-mono">AI API</span>
<span className="text-xs">|</span>
<span className="text-[10px] font-mono">llm.hanzo.ai</span>
<span className="text-[10px] font-mono">api.hanzo.ai/v1</span>
</div>
</div>
</section>
@@ -22,9 +22,9 @@ Activities are the **building blocks of work** in Hanzo Tasks. They perform the
<Tab value="Go">
```go
func CallLLM(ctx context.Context, prompt string) (LLMResponse, error) {
resp, err := http.Post("https://llm.hanzo.ai/v1/chat/completions",
resp, err := http.Post("https://api.hanzo.ai/v1/chat/completions",
"application/json",
strings.NewReader(fmt.Sprintf(`{"model":"zen-70b","messages":[{"role":"user","content":"%s"}]}`, prompt)),
strings.NewReader(fmt.Sprintf(`{"model":"zen5","messages":[{"role":"user","content":"%s"}]}`, prompt)),
)
if err != nil {
return LLMResponse{}, fmt.Errorf("llm request failed: %w", err)
@@ -46,9 +46,9 @@ func CallLLM(ctx context.Context, prompt string) (LLMResponse, error) {
async def call_llm(prompt: str) -> LLMResponse:
async with httpx.AsyncClient() as client:
resp = await client.post(
"https://llm.hanzo.ai/v1/chat/completions",
"https://api.hanzo.ai/v1/chat/completions",
json={
"model": "zen-70b",
"model": "zen5",
"messages": [{"role": "user", "content": prompt}],
},
)
+5 -5
View File
@@ -45,14 +45,14 @@ const MODELS = [
gguf: 'https://huggingface.co/zenlm/zen-pro-32b',
},
{
id: 'zen-coder',
id: 'zen5-coder',
name: 'Zen Coder',
size: '32B',
ram: '~20 GB RAM',
hf: 'zenlm/zen-coder',
ollama: 'ollama run hf.co/zenlm/zen-coder',
lmstudio: 'zenlm/zen-coder',
gguf: 'https://huggingface.co/zenlm/zen-coder',
hf: 'zenlm/zen5-coder',
ollama: 'ollama run hf.co/zenlm/zen5-coder',
lmstudio: 'zenlm/zen5-coder',
gguf: 'https://huggingface.co/zenlm/zen5-coder',
},
{
id: 'zen-max',
+1 -1
View File
@@ -265,7 +265,7 @@ export default function HomePage() {
<OpenModelCard name="Zen" id="zen" hf="zenlm/zen-8b" size="832B" ctx="32K" arch="Dense" ollama="hf.co/zenlm/zen-8b" />
<OpenModelCard name="Zen Pro" id="zen-pro" hf="zenlm/zen-pro-32b" size="32B" ctx="32K" arch="Dense" ollama="hf.co/zenlm/zen-pro-32b" />
<OpenModelCard name="Zen Max" id="zen-max" hf="zenlm/zen-max" size="235B (22B active)" ctx="131K" arch="MoE" ollama="hf.co/zenlm/zen-max" />
<OpenModelCard name="Zen Coder" id="zen-coder" hf="zenlm/zen-coder" size="32B" ctx="131K" arch="Dense" ollama="hf.co/zenlm/zen-coder" />
<OpenModelCard name="Zen Coder" id="zen5-coder" hf="zenlm/zen5-coder" size="32B" ctx="131K" arch="Dense" ollama="hf.co/zenlm/zen5-coder" />
</div>
</div>
</section>
@@ -40,10 +40,10 @@ pip install sglang[all]
| Model | Minimum VRAM | Recommended |
|-------|--------------|-------------|
| zen-nano | 2GB | 4GB |
| zen-coder-4b | 8GB | 16GB |
| zen-coder-flash | 24GB | 48GB |
| zen5-coder-4b | 8GB | 16GB |
| zen5-coder-flash | 24GB | 48GB |
| zen-max | 160GB | 320GB |
<Callout type="info">
zen-coder-flash uses MoE architecture with 31B total params but only 3B active, making it efficient for its capability level.
zen5-coder-flash uses MoE architecture with 31B total params but only 3B active, making it efficient for its capability level.
</Callout>
@@ -37,7 +37,7 @@ The easiest way to use Zen models:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "zenlm/zen-coder-flash"
model_id = "zenlm/zen5-coder-flash"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
@@ -57,7 +57,7 @@ print(tokenizer.decode(outputs[0], skip_special_tokens=True))
For high-throughput production serving:
```bash
vllm serve zenlm/zen-coder-flash \
vllm serve zenlm/zen5-coder-flash \
--tensor-parallel-size 4 \
--enable-auto-tool-choice
```
@@ -68,7 +68,7 @@ With EAGLE speculative decoding:
```bash
python -m sglang.launch_server \
--model-path zenlm/zen-coder-flash \
--model-path zenlm/zen5-coder-flash \
--tp-size 4 \
--speculative-algorithm EAGLE
```
@@ -80,7 +80,7 @@ Optimized for M1/M2/M3 Macs:
```python
from mlx_lm import load, generate
model, tokenizer = load("zenlm/zen-coder-flash")
model, tokenizer = load("zenlm/zen5-coder-flash")
response = generate(model, tokenizer, prompt="Write a Rust function for quicksort", max_tokens=256)
print(response)
```
+2 -2
View File
@@ -33,7 +33,7 @@ Latest generation production models with MoDE architecture. The recommended choi
Specialized models for code generation, review, debugging, and agentic programming.
<ModelTable ids={["zen4-coder", "zen4-coder-flash", "zen4-coder-pro", "zen-coder", "zen-coder-flash", "zen-code"]} showArch />
<ModelTable ids={["zen4-coder", "zen4-coder-flash", "zen4-coder-pro", "zen5-coder", "zen5-coder-flash", "zen-code"]} showArch />
---
@@ -81,7 +81,7 @@ General-purpose open-weight models available on [HuggingFace](https://huggingfac
Vision-language and multimodal open-weight models.
<ModelTable ids={["zen-vl", "zen-omni"]} showPricing={false} />
<ModelTable ids={["zen-vl", "zen5"]} showPricing={false} />
---
@@ -77,5 +77,5 @@ else:
## See Also
- [zen4](/docs/models/zen4) -- 744B MoE flagship model
- [zen-coder](/docs/models/zen-coder) -- 32B code-specialized model
- [zen5-coder](/docs/models/zen5-coder) -- 32B code-specialized model
- [zen-pro](/docs/models/zen-pro) -- 32B professional foundation model
@@ -7,7 +7,7 @@ description: Legacy 14B dense code model for general programming tasks.
**Code (Legacy)**
A 14B dense transformer for code generation and understanding. This is a legacy model -- for new projects, consider [zen-coder](/docs/models/zen-coder) (32B) or [zen4-coder](/docs/models/zen4-coder) (480B MoE).
A 14B dense transformer for code generation and understanding. This is a legacy model -- for new projects, consider [zen5-coder](/docs/models/zen5-coder) (32B) or [zen4-coder](/docs/models/zen4-coder) (480B MoE).
## Specifications
@@ -63,6 +63,6 @@ print(response.choices[0].message.content)
## See Also
- [zen-coder](/docs/models/zen-coder) -- 32B recommended replacement
- [zen-coder-flash](/docs/models/zen-coder-flash) -- 7B low-latency alternative
- [zen5-coder](/docs/models/zen5-coder) -- 32B recommended replacement
- [zen5-coder-flash](/docs/models/zen5-coder-flash) -- 7B low-latency alternative
- [zen4-coder](/docs/models/zen4-coder) -- 480B MoE flagship code model
@@ -1,9 +1,9 @@
---
title: zen-coder-flash
title: zen5-coder-flash
description: Lightweight 7B dense model for low-latency code completions.
---
# zen-coder-flash
# zen5-coder-flash
**Code**
@@ -13,12 +13,12 @@ A 7B dense transformer optimized for low-latency code completions. Designed for
| Property | Value |
|----------|-------|
| **Model ID** | `zen-coder-flash` |
| **Model ID** | `zen5-coder-flash` |
| **Parameters** | 7B |
| **Architecture** | Dense |
| **Context Window** | 32K tokens |
| **Status** | Available |
| **HuggingFace** | [zenlm/zen-coder-flash](https://huggingface.co/zenlm/zen-coder-flash) |
| **HuggingFace** | [zenlm/zen5-coder-flash](https://huggingface.co/zenlm/zen5-coder-flash) |
## Capabilities
@@ -40,8 +40,8 @@ pip install transformers torch
```python
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("zenlm/zen-coder-flash")
model = AutoModelForCausalLM.from_pretrained("zenlm/zen-coder-flash")
tokenizer = AutoTokenizer.from_pretrained("zenlm/zen5-coder-flash")
model = AutoModelForCausalLM.from_pretrained("zenlm/zen5-coder-flash")
inputs = tokenizer("def fibonacci(n):\n ", return_tensors="pt")
outputs = model.generate(**inputs, max_new_tokens=128)
@@ -56,7 +56,7 @@ from hanzoai import Hanzo
client = Hanzo(api_key="hk-your-api-key")
response = client.chat.completions.create(
model="zen-coder-flash",
model="zen5-coder-flash",
messages=[{"role": "user", "content": "Complete this function:\ndef binary_search(arr, target):\n "}],
)
print(response.choices[0].message.content)
@@ -64,6 +64,6 @@ print(response.choices[0].message.content)
## See Also
- [zen-coder](/docs/models/zen-coder) -- 32B full code model
- [zen5-coder](/docs/models/zen5-coder) -- 32B full code model
- [zen-code](/docs/models/zen-code) -- 14B legacy code model
- [zen4-coder-flash](/docs/models/zen4-coder-flash) -- 30B MoE fast code model
@@ -1,9 +1,9 @@
---
title: zen-coder
title: zen5-coder
description: 32B dense code model with 131K context for multi-language development.
---
# zen-coder
# zen5-coder
**Code**
@@ -13,12 +13,12 @@ A 32B dense transformer trained for software engineering. Supports multi-languag
| Property | Value |
|----------|-------|
| **Model ID** | `zen-coder` |
| **Model ID** | `zen5-coder` |
| **Parameters** | 32B |
| **Architecture** | Dense |
| **Context Window** | 131K tokens |
| **Status** | Available |
| **HuggingFace** | [zenlm/zen-coder](https://huggingface.co/zenlm/zen-coder) |
| **HuggingFace** | [zenlm/zen5-coder](https://huggingface.co/zenlm/zen5-coder) |
## Capabilities
@@ -40,8 +40,8 @@ pip install transformers torch
```python
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("zenlm/zen-coder")
model = AutoModelForCausalLM.from_pretrained("zenlm/zen-coder", device_map="auto")
tokenizer = AutoTokenizer.from_pretrained("zenlm/zen5-coder")
model = AutoModelForCausalLM.from_pretrained("zenlm/zen5-coder", device_map="auto")
inputs = tokenizer("Write a Python function to merge two sorted lists:", return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=256)
@@ -56,7 +56,7 @@ from hanzoai import Hanzo
client = Hanzo(api_key="hk-your-api-key")
response = client.chat.completions.create(
model="zen-coder",
model="zen5-coder",
messages=[{"role": "user", "content": "Write a Go HTTP server with graceful shutdown."}],
)
print(response.choices[0].message.content)
@@ -65,5 +65,5 @@ print(response.choices[0].message.content)
## See Also
- [zen4-coder](/docs/models/zen4-coder) -- 480B MoE code model
- [zen-coder-flash](/docs/models/zen-coder-flash) -- 7B low-latency code completions
- [zen5-coder-flash](/docs/models/zen5-coder-flash) -- 7B low-latency code completions
- [zen-code](/docs/models/zen-code) -- 14B legacy code model
@@ -1,9 +1,9 @@
---
title: zen-omni
title: zen5
description: 72B dense hypermodal model supporting text, vision, audio, and code.
---
# zen-omni
# zen5
**Hypermodal**
@@ -13,13 +13,13 @@ A 72B dense transformer that unifies text, vision, audio, and code in a single m
| Property | Value |
|----------|-------|
| **Model ID** | `zen-omni` |
| **Model ID** | `zen5` |
| **Parameters** | 72B |
| **Architecture** | Dense Multimodal |
| **Context Window** | 131K tokens |
| **Modalities** | Text, Vision, Audio, Code |
| **Status** | Available |
| **HuggingFace** | [zenlm/zen-omni](https://huggingface.co/zenlm/zen-omni) |
| **HuggingFace** | [zenlm/zen5](https://huggingface.co/zenlm/zen5) |
## Capabilities
@@ -41,8 +41,8 @@ pip install transformers torch pillow
```python
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("zenlm/zen-omni", trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained("zenlm/zen-omni", trust_remote_code=True, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained("zenlm/zen5", trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained("zenlm/zen5", trust_remote_code=True, device_map="auto")
inputs = tokenizer("Analyze this data and provide insights:", return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=512)
@@ -57,7 +57,7 @@ from hanzoai import Hanzo
client = Hanzo(api_key="hk-your-api-key")
response = client.chat.completions.create(
model="zen-omni",
model="zen5",
messages=[{
"role": "user",
"content": [
@@ -54,4 +54,4 @@ response = client.chat.completions.create(
- [zen-director](/docs/models/zen-director) -- Text-to-video generation
- [zen-video-i2v](/docs/models/zen-video-i2v) -- Image-to-video animation
- [zen-omni](/docs/models/zen-omni) -- Hypermodal understanding
- [zen5](/docs/models/zen5) -- Hypermodal understanding
+1 -1
View File
@@ -76,5 +76,5 @@ print(response.choices[0].message.content)
## See Also
- [zen3-vl](/docs/models/zen3-vl) -- 30B MoE vision-language model
- [zen-omni](/docs/models/zen-omni) -- 72B hypermodal (text+vision+audio+code)
- [zen5](/docs/models/zen5) -- 72B hypermodal (text+vision+audio+code)
- [zen3-omni](/docs/models/zen3-omni) -- 200B multimodal model
@@ -54,4 +54,4 @@ response = client.chat.completions.create(
- [zen-3d](/docs/models/zen-3d) -- 3D asset generation
- [zen-world](/docs/models/zen-world) -- World simulation
- [zen-omni](/docs/models/zen-omni) -- Hypermodal understanding
- [zen5](/docs/models/zen5) -- Hypermodal understanding
@@ -20,8 +20,8 @@ Training config at `training/configs/8xh200.yaml`:
```yaml
# Model
model_name: zenlm/zen-coder-flash
output_dir: ./zen-coder-flash-lora
model_name: zenlm/zen5-coder-flash
output_dir: ./zen5-coder-flash-lora
# Hardware
num_gpus: 8
@@ -54,8 +54,8 @@ dataset: hanzoai/zen-agentic-dataset-private
```bash
# Clone the repo
git clone https://github.com/zenlm/zen-coder-flash
cd zen-coder-flash
git clone https://github.com/zenlm/zen5-coder-flash
cd zen5-coder-flash
# Dry run
python training/launch_training.py --dry-run
@@ -73,7 +73,7 @@ The launcher generates a SLURM job script:
```bash
#!/bin/bash
#SBATCH --job-name=zen-coder-flash
#SBATCH --job-name=zen5-coder-flash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --gpus-per-node=8
+3 -3
View File
@@ -5,7 +5,7 @@ description: Train locally with NVIDIA GPUs
# CUDA Training (Local GPU)
Train zen-coder-flash on NVIDIA GPUs with QLoRA.
Train zen5-coder-flash on NVIDIA GPUs with QLoRA.
## Requirements
@@ -23,8 +23,8 @@ pip install torch transformers accelerate peft bitsandbytes datasets
```bash
# Clone the repo
git clone https://github.com/zenlm/zen-coder-flash
cd zen-coder-flash
git clone https://github.com/zenlm/zen5-coder-flash
cd zen5-coder-flash
# Train with QLoRA
python training/train_cuda.py
+3 -3
View File
@@ -5,7 +5,7 @@ description: Train on Apple Silicon with MLX
# MLX Training (Apple Silicon)
Train zen-coder-flash on M1/M2/M3 Macs using MLX.
Train zen5-coder-flash on M1/M2/M3 Macs using MLX.
## Requirements
@@ -23,8 +23,8 @@ pip install mlx mlx-lm
```bash
# Clone the repo
git clone https://github.com/zenlm/zen-coder-flash
cd zen-coder-flash
git clone https://github.com/zenlm/zen5-coder-flash
cd zen5-coder-flash
# Train with LoRA
python training/train_mlx.py
@@ -32,7 +32,7 @@ Training is powered by [Zoo Gym](https://github.com/zooai/gym):
## Repository Structure
```
zen-coder-flash/
zen5-coder-flash/
├── training/
│ ├── configs/
│ │ └── 8xh200.yaml # Nebius 8x H200 config
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -55,7 +55,7 @@ import * as __fd_glob_73 from "../content/docs/models/zen-translator.mdx?collect
import * as __fd_glob_72 from "../content/docs/models/zen-scribe.mdx?collection=docs"
import * as __fd_glob_71 from "../content/docs/models/zen-reranker.mdx?collection=docs"
import * as __fd_glob_70 from "../content/docs/models/zen-pro.mdx?collection=docs"
import * as __fd_glob_69 from "../content/docs/models/zen-omni.mdx?collection=docs"
import * as __fd_glob_69 from "../content/docs/models/zen5.mdx?collection=docs"
import * as __fd_glob_68 from "../content/docs/models/zen-next.mdx?collection=docs"
import * as __fd_glob_67 from "../content/docs/models/zen-nano.mdx?collection=docs"
import * as __fd_glob_66 from "../content/docs/models/zen-musician.mdx?collection=docs"
@@ -71,8 +71,8 @@ import * as __fd_glob_57 from "../content/docs/models/zen-dub.mdx?collection=doc
import * as __fd_glob_56 from "../content/docs/models/zen-dub-live.mdx?collection=docs"
import * as __fd_glob_55 from "../content/docs/models/zen-director.mdx?collection=docs"
import * as __fd_glob_54 from "../content/docs/models/zen-designer.mdx?collection=docs"
import * as __fd_glob_53 from "../content/docs/models/zen-coder.mdx?collection=docs"
import * as __fd_glob_52 from "../content/docs/models/zen-coder-flash.mdx?collection=docs"
import * as __fd_glob_53 from "../content/docs/models/zen5-coder.mdx?collection=docs"
import * as __fd_glob_52 from "../content/docs/models/zen5-coder-flash.mdx?collection=docs"
import * as __fd_glob_51 from "../content/docs/models/zen-code.mdx?collection=docs"
import * as __fd_glob_50 from "../content/docs/models/zen-artist.mdx?collection=docs"
import * as __fd_glob_49 from "../content/docs/models/zen-artist-edit.mdx?collection=docs"
@@ -135,4 +135,4 @@ const create = server<typeof Config, import("@hanzo/docs-mdx/runtime/types").Int
export const blog = await create.docLazy("blog", "content/blog", {"7680-dim-embeddings.mdx": __fd_glob_0, "agent-nfts.mdx": __fd_glob_1, "bitdelta-behavioral-compression.mdx": __fd_glob_2, "case-for-decentralized-science.mdx": __fd_glob_3, "case-for-desci.mdx": __fd_glob_4, "chinese-clip.mdx": __fd_glob_5, "continual-learning-sure-opcm.mdx": __fd_glob_6, "decentralized-compute.mdx": __fd_glob_7, "drop-upcycling-zen-mode.mdx": __fd_glob_8, "embedding-spaces-7680-dimensions.mdx": __fd_glob_9, "experience-ledgers.mdx": __fd_glob_10, "federated-learning-without-compromise.mdx": __fd_glob_11, "federated-learning.mdx": __fd_glob_12, "future-of-open-ai.mdx": __fd_glob_13, "global-load-balance.mdx": __fd_glob_14, "grpo-group-relative-policy-optimization.mdx": __fd_glob_15, "grpo.mdx": __fd_glob_16, "gt-qlora-moe-abliteration.mdx": __fd_glob_17, "introducing-zen.mdx": __fd_glob_18, "ofa.mdx": __fd_glob_19, "ofasys.mdx": __fd_glob_20, "open-training-hanzo-network.mdx": __fd_glob_21, "open-weights-philosophy.mdx": __fd_glob_22, "proof-of-ai.mdx": __fd_glob_23, "training-gym.mdx": __fd_glob_24, "training-llms-collective-intelligence.mdx": __fd_glob_25, "zen-3.mdx": __fd_glob_26, "zen-collaboration.mdx": __fd_glob_27, "zen-lm-launch.mdx": __fd_glob_28, "zen-mode-architecture.mdx": __fd_glob_29, "zen-reranker.mdx": __fd_glob_30, "zen4-ultra.mdx": __fd_glob_31, "zen5-release.mdx": __fd_glob_32, "zips-governance.mdx": __fd_glob_33, "zoo-foundation-launch.mdx": __fd_glob_34, }, {"7680-dim-embeddings.mdx": () => import("../content/blog/7680-dim-embeddings.mdx?collection=blog"), "agent-nfts.mdx": () => import("../content/blog/agent-nfts.mdx?collection=blog"), "bitdelta-behavioral-compression.mdx": () => import("../content/blog/bitdelta-behavioral-compression.mdx?collection=blog"), "case-for-decentralized-science.mdx": () => import("../content/blog/case-for-decentralized-science.mdx?collection=blog"), "case-for-desci.mdx": () => import("../content/blog/case-for-desci.mdx?collection=blog"), "chinese-clip.mdx": () => import("../content/blog/chinese-clip.mdx?collection=blog"), "continual-learning-sure-opcm.mdx": () => import("../content/blog/continual-learning-sure-opcm.mdx?collection=blog"), "decentralized-compute.mdx": () => import("../content/blog/decentralized-compute.mdx?collection=blog"), "drop-upcycling-zen-mode.mdx": () => import("../content/blog/drop-upcycling-zen-mode.mdx?collection=blog"), "embedding-spaces-7680-dimensions.mdx": () => import("../content/blog/embedding-spaces-7680-dimensions.mdx?collection=blog"), "experience-ledgers.mdx": () => import("../content/blog/experience-ledgers.mdx?collection=blog"), "federated-learning-without-compromise.mdx": () => import("../content/blog/federated-learning-without-compromise.mdx?collection=blog"), "federated-learning.mdx": () => import("../content/blog/federated-learning.mdx?collection=blog"), "future-of-open-ai.mdx": () => import("../content/blog/future-of-open-ai.mdx?collection=blog"), "global-load-balance.mdx": () => import("../content/blog/global-load-balance.mdx?collection=blog"), "grpo-group-relative-policy-optimization.mdx": () => import("../content/blog/grpo-group-relative-policy-optimization.mdx?collection=blog"), "grpo.mdx": () => import("../content/blog/grpo.mdx?collection=blog"), "gt-qlora-moe-abliteration.mdx": () => import("../content/blog/gt-qlora-moe-abliteration.mdx?collection=blog"), "introducing-zen.mdx": () => import("../content/blog/introducing-zen.mdx?collection=blog"), "ofa.mdx": () => import("../content/blog/ofa.mdx?collection=blog"), "ofasys.mdx": () => import("../content/blog/ofasys.mdx?collection=blog"), "open-training-hanzo-network.mdx": () => import("../content/blog/open-training-hanzo-network.mdx?collection=blog"), "open-weights-philosophy.mdx": () => import("../content/blog/open-weights-philosophy.mdx?collection=blog"), "proof-of-ai.mdx": () => import("../content/blog/proof-of-ai.mdx?collection=blog"), "training-gym.mdx": () => import("../content/blog/training-gym.mdx?collection=blog"), "training-llms-collective-intelligence.mdx": () => import("../content/blog/training-llms-collective-intelligence.mdx?collection=blog"), "zen-3.mdx": () => import("../content/blog/zen-3.mdx?collection=blog"), "zen-collaboration.mdx": () => import("../content/blog/zen-collaboration.mdx?collection=blog"), "zen-lm-launch.mdx": () => import("../content/blog/zen-lm-launch.mdx?collection=blog"), "zen-mode-architecture.mdx": () => import("../content/blog/zen-mode-architecture.mdx?collection=blog"), "zen-reranker.mdx": () => import("../content/blog/zen-reranker.mdx?collection=blog"), "zen4-ultra.mdx": () => import("../content/blog/zen4-ultra.mdx?collection=blog"), "zen5-release.mdx": () => import("../content/blog/zen5-release.mdx?collection=blog"), "zips-governance.mdx": () => import("../content/blog/zips-governance.mdx?collection=blog"), "zoo-foundation-launch.mdx": () => import("../content/blog/zoo-foundation-launch.mdx?collection=blog"), });
export const docs = await create.docs("docs", "content/docs", {"meta.json": __fd_glob_125, }, {"datasets.mdx": __fd_glob_35, "index.mdx": __fd_glob_36, "models.mdx": __fd_glob_37, "training.mdx": __fd_glob_38, "api/chat-completions.mdx": __fd_glob_39, "api/embeddings.mdx": __fd_glob_40, "api/index.mdx": __fd_glob_41, "api/messages.mdx": __fd_glob_42, "api/models.mdx": __fd_glob_43, "api/pricing.mdx": __fd_glob_44, "getting-started/installation.mdx": __fd_glob_45, "getting-started/quickstart.mdx": __fd_glob_46, "models/zen-3d.mdx": __fd_glob_47, "models/zen-agent.mdx": __fd_glob_48, "models/zen-artist-edit.mdx": __fd_glob_49, "models/zen-artist.mdx": __fd_glob_50, "models/zen-code.mdx": __fd_glob_51, "models/zen-coder-flash.mdx": __fd_glob_52, "models/zen-coder.mdx": __fd_glob_53, "models/zen-designer.mdx": __fd_glob_54, "models/zen-director.mdx": __fd_glob_55, "models/zen-dub-live.mdx": __fd_glob_56, "models/zen-dub.mdx": __fd_glob_57, "models/zen-eco.mdx": __fd_glob_58, "models/zen-embedding.mdx": __fd_glob_59, "models/zen-foley.mdx": __fd_glob_60, "models/zen-guard-gen.mdx": __fd_glob_61, "models/zen-guard-stream.mdx": __fd_glob_62, "models/zen-guard.mdx": __fd_glob_63, "models/zen-live.mdx": __fd_glob_64, "models/zen-max.mdx": __fd_glob_65, "models/zen-musician.mdx": __fd_glob_66, "models/zen-nano.mdx": __fd_glob_67, "models/zen-next.mdx": __fd_glob_68, "models/zen-omni.mdx": __fd_glob_69, "models/zen-pro.mdx": __fd_glob_70, "models/zen-reranker.mdx": __fd_glob_71, "models/zen-scribe.mdx": __fd_glob_72, "models/zen-translator.mdx": __fd_glob_73, "models/zen-video-i2v.mdx": __fd_glob_74, "models/zen-video.mdx": __fd_glob_75, "models/zen-vl.mdx": __fd_glob_76, "models/zen-voyager.mdx": __fd_glob_77, "models/zen-world.mdx": __fd_glob_78, "models/zen.mdx": __fd_glob_79, "models/zen3-asr-v1.mdx": __fd_glob_80, "models/zen3-asr.mdx": __fd_glob_81, "models/zen3-audio-fast.mdx": __fd_glob_82, "models/zen3-audio.mdx": __fd_glob_83, "models/zen3-embedding-medium.mdx": __fd_glob_84, "models/zen3-embedding-small.mdx": __fd_glob_85, "models/zen3-embedding.mdx": __fd_glob_86, "models/zen3-guard.mdx": __fd_glob_87, "models/zen3-image-dev.mdx": __fd_glob_88, "models/zen3-image-fast.mdx": __fd_glob_89, "models/zen3-image-jp.mdx": __fd_glob_90, "models/zen3-image-max.mdx": __fd_glob_91, "models/zen3-image-playground.mdx": __fd_glob_92, "models/zen3-image-sdxl.mdx": __fd_glob_93, "models/zen3-image-ssd.mdx": __fd_glob_94, "models/zen3-image.mdx": __fd_glob_95, "models/zen3-nano.mdx": __fd_glob_96, "models/zen3-omni.mdx": __fd_glob_97, "models/zen3-reranker-medium.mdx": __fd_glob_98, "models/zen3-reranker-small.mdx": __fd_glob_99, "models/zen3-reranker.mdx": __fd_glob_100, "models/zen3-tts-fast.mdx": __fd_glob_101, "models/zen3-tts-hd.mdx": __fd_glob_102, "models/zen3-tts.mdx": __fd_glob_103, "models/zen3-vl.mdx": __fd_glob_104, "models/zen4-coder-flash.mdx": __fd_glob_105, "models/zen4-coder-pro.mdx": __fd_glob_106, "models/zen4-coder.mdx": __fd_glob_107, "models/zen4-max.mdx": __fd_glob_108, "models/zen4-mini.mdx": __fd_glob_109, "models/zen4-pro.mdx": __fd_glob_110, "models/zen4-thinking.mdx": __fd_glob_111, "models/zen4-ultra.mdx": __fd_glob_112, "models/zen4.1.mdx": __fd_glob_113, "models/zen4.mdx": __fd_glob_114, "models/zen5-coder.mdx": __fd_glob_115, "models/zen5-max.mdx": __fd_glob_116, "models/zen5-mini.mdx": __fd_glob_117, "models/zen5-pro.mdx": __fd_glob_118, "models/zen5-ultra.mdx": __fd_glob_119, "models/zen5.mdx": __fd_glob_120, "training/cloud.mdx": __fd_glob_121, "training/cuda.mdx": __fd_glob_122, "training/mlx.mdx": __fd_glob_123, "training/overview.mdx": __fd_glob_124, });
export const docs = await create.docs("docs", "content/docs", {"meta.json": __fd_glob_125, }, {"datasets.mdx": __fd_glob_35, "index.mdx": __fd_glob_36, "models.mdx": __fd_glob_37, "training.mdx": __fd_glob_38, "api/chat-completions.mdx": __fd_glob_39, "api/embeddings.mdx": __fd_glob_40, "api/index.mdx": __fd_glob_41, "api/messages.mdx": __fd_glob_42, "api/models.mdx": __fd_glob_43, "api/pricing.mdx": __fd_glob_44, "getting-started/installation.mdx": __fd_glob_45, "getting-started/quickstart.mdx": __fd_glob_46, "models/zen-3d.mdx": __fd_glob_47, "models/zen-agent.mdx": __fd_glob_48, "models/zen-artist-edit.mdx": __fd_glob_49, "models/zen-artist.mdx": __fd_glob_50, "models/zen-code.mdx": __fd_glob_51, "models/zen5-coder-flash.mdx": __fd_glob_52, "models/zen5-coder.mdx": __fd_glob_53, "models/zen-designer.mdx": __fd_glob_54, "models/zen-director.mdx": __fd_glob_55, "models/zen-dub-live.mdx": __fd_glob_56, "models/zen-dub.mdx": __fd_glob_57, "models/zen-eco.mdx": __fd_glob_58, "models/zen-embedding.mdx": __fd_glob_59, "models/zen-foley.mdx": __fd_glob_60, "models/zen-guard-gen.mdx": __fd_glob_61, "models/zen-guard-stream.mdx": __fd_glob_62, "models/zen-guard.mdx": __fd_glob_63, "models/zen-live.mdx": __fd_glob_64, "models/zen-max.mdx": __fd_glob_65, "models/zen-musician.mdx": __fd_glob_66, "models/zen-nano.mdx": __fd_glob_67, "models/zen-next.mdx": __fd_glob_68, "models/zen5.mdx": __fd_glob_69, "models/zen-pro.mdx": __fd_glob_70, "models/zen-reranker.mdx": __fd_glob_71, "models/zen-scribe.mdx": __fd_glob_72, "models/zen-translator.mdx": __fd_glob_73, "models/zen-video-i2v.mdx": __fd_glob_74, "models/zen-video.mdx": __fd_glob_75, "models/zen-vl.mdx": __fd_glob_76, "models/zen-voyager.mdx": __fd_glob_77, "models/zen-world.mdx": __fd_glob_78, "models/zen.mdx": __fd_glob_79, "models/zen3-asr-v1.mdx": __fd_glob_80, "models/zen3-asr.mdx": __fd_glob_81, "models/zen3-audio-fast.mdx": __fd_glob_82, "models/zen3-audio.mdx": __fd_glob_83, "models/zen3-embedding-medium.mdx": __fd_glob_84, "models/zen3-embedding-small.mdx": __fd_glob_85, "models/zen3-embedding.mdx": __fd_glob_86, "models/zen3-guard.mdx": __fd_glob_87, "models/zen3-image-dev.mdx": __fd_glob_88, "models/zen3-image-fast.mdx": __fd_glob_89, "models/zen3-image-jp.mdx": __fd_glob_90, "models/zen3-image-max.mdx": __fd_glob_91, "models/zen3-image-playground.mdx": __fd_glob_92, "models/zen3-image-sdxl.mdx": __fd_glob_93, "models/zen3-image-ssd.mdx": __fd_glob_94, "models/zen3-image.mdx": __fd_glob_95, "models/zen3-nano.mdx": __fd_glob_96, "models/zen3-omni.mdx": __fd_glob_97, "models/zen3-reranker-medium.mdx": __fd_glob_98, "models/zen3-reranker-small.mdx": __fd_glob_99, "models/zen3-reranker.mdx": __fd_glob_100, "models/zen3-tts-fast.mdx": __fd_glob_101, "models/zen3-tts-hd.mdx": __fd_glob_102, "models/zen3-tts.mdx": __fd_glob_103, "models/zen3-vl.mdx": __fd_glob_104, "models/zen4-coder-flash.mdx": __fd_glob_105, "models/zen4-coder-pro.mdx": __fd_glob_106, "models/zen4-coder.mdx": __fd_glob_107, "models/zen4-max.mdx": __fd_glob_108, "models/zen4-mini.mdx": __fd_glob_109, "models/zen4-pro.mdx": __fd_glob_110, "models/zen4-thinking.mdx": __fd_glob_111, "models/zen4-ultra.mdx": __fd_glob_112, "models/zen4.1.mdx": __fd_glob_113, "models/zen4.mdx": __fd_glob_114, "models/zen5-coder.mdx": __fd_glob_115, "models/zen5-max.mdx": __fd_glob_116, "models/zen5-mini.mdx": __fd_glob_117, "models/zen5-pro.mdx": __fd_glob_118, "models/zen5-ultra.mdx": __fd_glob_119, "models/zen5.mdx": __fd_glob_120, "training/cloud.mdx": __fd_glob_121, "training/cuda.mdx": __fd_glob_122, "training/mlx.mdx": __fd_glob_123, "training/overview.mdx": __fd_glob_124, });
+2 -2
View File
@@ -24,7 +24,7 @@ export const families: ModelFamily[] = [
name: 'Code',
description: 'Specialized models for code generation, review, and debugging.',
icon: 'Code',
models: ['zen4-coder', 'zen4-coder-flash', 'zen4-coder-pro', 'zen-coder', 'zen-coder-flash', 'zen-code'],
models: ['zen4-coder', 'zen4-coder-flash', 'zen4-coder-pro', 'zen5-coder', 'zen5-coder-flash', 'zen-code'],
},
{
id: 'zen3',
@@ -76,7 +76,7 @@ export const families: ModelFamily[] = [
name: 'Vision (Open Weights)',
description: 'Vision-language and multimodal open-weight models.',
icon: 'Eye',
models: ['zen-vl', 'zen-omni'],
models: ['zen-vl', 'zen5'],
},
{
id: 'safety',
+6 -6
View File
@@ -1004,7 +1004,7 @@ export const zenNext: ZenModel = {
// ---------------------------------------------------------------------------
export const zenCoder: ZenModel = {
id: 'zen-coder',
id: 'zen5-coder',
name: 'Zen Coder',
fullName: 'Zen Coder — Code Generation',
description: 'Baseline code model for generation and completions.',
@@ -1016,13 +1016,13 @@ export const zenCoder: ZenModel = {
pricing: null,
features: ['131K context', 'Multi-language'],
status: 'available',
huggingface: 'https://huggingface.co/zenlm/zen-coder',
huggingface: 'https://huggingface.co/zenlm/zen5-coder',
github: null,
aliases: [],
}
export const zenCoderFlash: ZenModel = {
id: 'zen-coder-flash',
id: 'zen5-coder-flash',
name: 'Zen Coder Flash',
fullName: 'Zen Coder Flash — Fast Code',
description: 'Fast code model for inline completions and suggestions.',
@@ -1034,7 +1034,7 @@ export const zenCoderFlash: ZenModel = {
pricing: null,
features: ['32K context', 'Low latency'],
status: 'available',
huggingface: 'https://huggingface.co/zenlm/zen-coder-flash',
huggingface: 'https://huggingface.co/zenlm/zen5-coder-flash',
github: null,
aliases: [],
}
@@ -1080,7 +1080,7 @@ export const zenVl: ZenModel = {
}
export const zenOmni: ZenModel = {
id: 'zen-omni',
id: 'zen5',
name: 'Zen Omni',
fullName: 'Zen Omni — Multimodal',
description: 'Hypermodal model combining text, vision, audio, and code.',
@@ -1092,7 +1092,7 @@ export const zenOmni: ZenModel = {
pricing: null,
features: ['131K context', 'Multimodal'],
status: 'available',
huggingface: 'https://huggingface.co/zenlm/zen-omni',
huggingface: 'https://huggingface.co/zenlm/zen5',
github: null,
aliases: [],
}