mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
Docs state the two API keys cloud mints, and the build proves it
docs.hanzo.ai documented three key types. Cloud mints two. cloud.APIKeyPrefixes is pk- and sk-; hk- and hz- have never existed and an unrecognized spelling is refused at the door, so a reader's first call failed asking for a credential nobody can issue. 149 files carried one. sk- was the subtler half: present, and described as an org-level provider credential when it resolves to the USER. A find-and-replace would have left that confidently wrong, so the pages that made a further claim -- a scope, a third family, a list of recognized prefixes -- were rewritten rather than swapped. Derived, not restated. openapi-doc gains Document.keys, read out of the /v1/keys prose the Go handler writes and zipdoc lifts into the document. gen-key-types renders that as an MDX fragment api-keys.mdx includes, so the table nobody may type is not typed. secretKey(doc) replaces the literal three generators each kept -- which is how three GENERATED pages also came to teach hk-. check-keys then holds every page to it, both directions: a prefix the document does not carry fails the build, and a class api-keys.mdx does not teach fails it too. Renaming pk- in cloud now stops the build in six places instead of publishing a stale page. Also measured against the live API and cluster, and corrected: /v1/keys does mint keys over HTTP (403 unauthenticated, not absent); Tasks is /v1/tasks on the one host, not gRPC elsewhere; the KMS example path 404s; and four KMSSecret manifests could not apply -- two named groups with no controller, and their spec was External Secrets' shape, not the CRD's five fields. Sidebar sits flush left. The docs grid was five columns centring a band, with the sidebar area spanning the leading gutter, so the nav began 176.5px in at 1920 and 496.5px in at 2560 -- inside its own bordered card. Three columns now: rails pinned, page between them. Measured at /docs/api-keys before -> after: 1920 176.5/232/1052/268/176.5 -> 232/1405/268, 2560 496.5/232/1052/268/496.5 -> 232/2045/268, nav at x=0. Phone 390 and tablet 820 unchanged (0/375/0 and 232/573/0, drawer opens, no page overflow); desktop collapse and restore verified.
This commit is contained in:
@@ -137,6 +137,14 @@ at docs.hanzo.ai/docs/contributing/docs-architecture). Summary:
|
||||
travels here unaltered); SDK reference from the ZAP SDK generator into
|
||||
`content/docs/sdks/<lang>/`. Every reader of the document goes through
|
||||
`scripts/openapi-doc.ts`, which owns the path — no script names the file.
|
||||
A generated MDX **fragment** is the third shape here, for a fact an authored
|
||||
page needs mid-sentence: `scripts/gen-key-types.ts` writes
|
||||
`generated/key-types.mdx` and `content/docs/api-keys.mdx` pulls it in with
|
||||
`<include>`. It sits outside `content/` because source.config globs
|
||||
`**/*.mdx` there and would give a fragment its own URL — and the path is
|
||||
relative to the INCLUDING file, so from `content/docs/` it is
|
||||
`../../generated/`. One directory short renders the page body empty with
|
||||
the frontmatter and chrome intact, which looks like a working page.
|
||||
3. **Ported** (upstream OSS, mirrored with attribution, `.gitignore`d) →
|
||||
`scripts/sync-project-docs.ts` into `content/docs/projects/<upstream>/`.
|
||||
Port, don't re-author. Carry upstream LICENSE + NOTICE. GPL stays GPL.
|
||||
@@ -161,6 +169,34 @@ zt-docs) predate the unified `content/docs/` model — migrate their content int
|
||||
`content/docs/<section>/` (or a `hanzo-docs/<team>` submodule) then archive the
|
||||
app.
|
||||
|
||||
## Two build guards, and why prose needs them
|
||||
|
||||
`scripts/pre-build.ts` ends with two checks that fail the build. Both exist for
|
||||
the same reason: a generated page cannot be wrong about the API, an authored one
|
||||
can, and a reader cannot tell which kind of page they are on.
|
||||
|
||||
- **`check-endpoints.ts`** — no page may name a `/v1/…` route on `api.hanzo.ai`
|
||||
that the document does not serve.
|
||||
- **`check-keys.ts`** — no page may spell an API key a way cloud does not mint,
|
||||
and `api-keys.mdx` must teach every class the document carries. Both
|
||||
directions, so renaming, adding or removing a key class in cloud stops the
|
||||
build here instead of publishing a stale page.
|
||||
|
||||
The key classes come from `Document.keys` (`openapi-doc.ts`), read out of the
|
||||
`/v1/keys` prose — cloud's own Go doc comments, lifted by zipdoc. `secretKey(doc)`
|
||||
is the one accessor generators use for "the key a server presents"; three of them
|
||||
had it as a literal, which is how three GENERATED pages came to teach `hk-`.
|
||||
|
||||
**What was wrong:** docs.hanzo.ai documented three key types — `hk-` "API Key",
|
||||
`sk-` "Secret Key", `hz-` "Widget Key". Cloud mints two (`cloud.APIKeyPrefixes`:
|
||||
`pk-`, `sk-`) and refuses anything else, so a reader's first call failed asking
|
||||
for a key nobody can issue. `hk-`/`hz-` were on 149 files. Note the shape of the
|
||||
mistake: `sk-` was *present* and *described wrongly* (as an org-level provider
|
||||
credential; it resolves to the USER), so a find-and-replace would have left the
|
||||
page confidently wrong. Cloud's `apps/platform/secretshape.go` still lists `hk-`
|
||||
in its secret-DETECTION table — harmless, it only scans, but it is where the
|
||||
invention came from.
|
||||
|
||||
## Branch Convention
|
||||
|
||||
- **`main`** — Production branch. docs.hanzo.ai is built from here and served
|
||||
@@ -290,6 +326,33 @@ import { DocsPage, DocsBody } from '@hanzo/docs-ui/layouts/docs/page';
|
||||
import defaultMdxComponents from '@hanzo/docs-ui/mdx';
|
||||
```
|
||||
|
||||
### The docs grid is three columns
|
||||
|
||||
`layouts/docs/slots/container.tsx` (both UI packages — they are parallel forks,
|
||||
so a change to one without the other diverges them): `var(--fd-sidebar-col)
|
||||
minmax(0, 1fr) var(--fd-toc-width)`. Rails pin to the edges, the page takes what
|
||||
is between them.
|
||||
|
||||
It was five columns, with `minmax(min-content, 1fr)` gutters centring a band
|
||||
capped at `--fd-layout-width`, and the sidebar AREA spanned the leading gutter as
|
||||
well as its own column. Measured on the live site at `/docs/api-keys`: at 1920
|
||||
the columns were `176.5 232 1052 268 176.5` and the nav — 232px, right-aligned in
|
||||
its area by `items-end` — began 176px from the left edge; at 2560 it began 496px
|
||||
in. The empty strip was inside the sidebar's own bordered, filled card, so every
|
||||
pixel a wider display gained went there. Now `232 1405 268` and `232 2045 268`,
|
||||
nav at x=0.
|
||||
|
||||
`--fd-layout-width` no longer bounds this grid. It was doing two jobs at once —
|
||||
bounding the reading measure and bounding the band — and the page slot already
|
||||
owns the first (`max-w-[900px]`), so what was left was only the job that made the
|
||||
gap. The article therefore stays 900px and does not move; what moved is the nav
|
||||
(flush left) and the toc (flush right), and the page COLUMN absorbed the gutters.
|
||||
Widening the article past 900px is a separate typography decision, untaken.
|
||||
|
||||
Narrow viewports are unchanged by construction: below `md`, `--fd-sidebar-width`
|
||||
is 0 and the sidebar is a fixed drawer outside the grid, and `--fd-toc-width` is
|
||||
0 until a toc exists.
|
||||
|
||||
### Layouts
|
||||
- `@hanzo/docs-ui/layouts/docs` - Standard docs layout
|
||||
- `@hanzo/docs-ui/layouts/home` - Homepage layout
|
||||
|
||||
@@ -37,3 +37,8 @@ content/docs/mcp-tools/
|
||||
# read from /v1/pricing; the snapshot it renders from IS tracked
|
||||
# (openapi-specs/pricing.json), so a builder with no egress still gets the page.
|
||||
content/docs/pricing.mdx
|
||||
|
||||
# Generated MDX fragments that pages <include> — see scripts/gen-key-types.ts.
|
||||
# Not under content/, because source.config globs `**/*.mdx` there and would
|
||||
# give a fragment its own URL.
|
||||
generated/
|
||||
|
||||
@@ -65,7 +65,7 @@ const DOORS = [
|
||||
external: false,
|
||||
cta: 'Read the API reference',
|
||||
lang: 'Request',
|
||||
code: 'curl https://api.hanzo.ai/v1/chat/completions \\\n -H "Authorization: Bearer hk-..." \\\n -d \'{"model":"enso","messages":[...]}\'',
|
||||
code: 'curl https://api.hanzo.ai/v1/chat/completions \\\n -H "Authorization: Bearer sk-..." \\\n -d \'{"model":"enso","messages":[...]}\'',
|
||||
},
|
||||
] as const;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Build, deploy, and run autonomous agents: configured personas with a model, a to
|
||||
|
||||
## Quickstart
|
||||
|
||||
Define an agent and run it with the SDK. The same `hk-*` key authenticates every call.
|
||||
Define an agent and run it with the SDK. The same `sk-*` key authenticates every call.
|
||||
|
||||
```typescript
|
||||
import { Agent, Runtime } from '@hanzo/agents'
|
||||
@@ -44,7 +44,7 @@ Agents also run over REST through the gateway. Point the SDK at `https://api.han
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/agents/runs \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"agent": "researcher",
|
||||
|
||||
@@ -1,133 +1,159 @@
|
||||
---
|
||||
icon: KeyRound
|
||||
title: API Keys
|
||||
description: Key types, scopes, creation, rotation, and production best practices for Hanzo API keys.
|
||||
description: The two Hanzo key types, which one may ship in a browser, and how to create, rotate and revoke them.
|
||||
---
|
||||
|
||||
# API Keys
|
||||
|
||||
Every request to the Hanzo platform is authenticated with a bearer token. This page covers the three key types, how to create and use them, and how to keep them safe.
|
||||
Every request to the Hanzo platform is authenticated with a bearer token. There
|
||||
are two kinds of key, they are not interchangeable, and the difference is what
|
||||
each one resolves to when the door reads it.
|
||||
|
||||
## Key Types and Prefixes
|
||||
## The two types
|
||||
|
||||
Hanzo uses key prefixes so you can identify a credential at a glance and catch accidental misuse.
|
||||
<include>../../generated/key-types.mdx</include>
|
||||
|
||||
| Prefix | Type | Where to Use | Scope |
|
||||
|--------|------|--------------|-------|
|
||||
| `hk-*` | **API Key** | Server-side code, CI/CD, scripts | Project-level. Full access to APIs within the project's permissions. |
|
||||
| `sk-*` | **Secret Key** | Backend services, provider configuration | Org-level. Used for upstream provider credentials and internal service auth. Never expose to clients. |
|
||||
| `hz-*` | **Widget Key** | Browser JavaScript, mobile apps | Project-level with restricted scopes. Safe to embed in client-side code. |
|
||||
### What each one resolves to
|
||||
|
||||
### API Keys (`hk-*`)
|
||||
A **secret key** resolves to **you**. The door looks it up and gets your user
|
||||
row, which makes it session-equivalent: anything you can do in the console, a
|
||||
holder of your `sk-` can do over HTTP. That is why it belongs on a server and
|
||||
nowhere else.
|
||||
|
||||
These are your primary credentials. One `hk-*` key gives access to all Hanzo services for the project it belongs to: AI API, search, storage, tasks, and everything else.
|
||||
A **publishable key** resolves to **your organization** and stops there — it
|
||||
never yields a principal, so it cannot read anything. It identifies; it does not
|
||||
authenticate. That is the property that makes it safe to ship in a browser
|
||||
bundle, and it is enforced at the door rather than promised: a `pk-` presented
|
||||
as a bearer credential is refused outright, no matter which endpoint it arrives
|
||||
at. One `pk-` covers analytics, product insights and error capture, which is why
|
||||
you do not need a separate key or a separate DSN for each.
|
||||
|
||||
```bash
|
||||
# All Hanzo API calls use the same header format
|
||||
curl -H "Authorization: Bearer hk-proj-abc123..." https://api.hanzo.ai/v1/models
|
||||
```
|
||||
A publishable key minted alongside a project names that project rather than the
|
||||
organization, so beacons from two sites attribute separately. It is the same
|
||||
spelling and the same rules.
|
||||
|
||||
### Secret Keys (`sk-*`)
|
||||
### They are two rows, not two halves of one key
|
||||
|
||||
Secret keys are org-scoped credentials used for provider-level operations: configuring upstream LLM providers, managing billing integrations, and inter-service communication. They carry elevated privileges and must never leave your backend.
|
||||
A user holds at most one key of each type, and IAM stores them as separate rows.
|
||||
So rotating the key in your browser bundle does not sign your servers out, and
|
||||
rotating your server key does not blank the analytics on your site. They fail
|
||||
independently, which is the point of having two.
|
||||
|
||||
### Widget Keys (`hz-*`)
|
||||
Asking for a type that does not exist is an error, not a default. The API will
|
||||
not hand you a secret key because you asked for something it did not recognize —
|
||||
handing an `sk-` to someone who wanted a browser key is exactly the accident the
|
||||
refusal exists to prevent.
|
||||
|
||||
Widget keys are designed for client-side use. They have restricted scopes -- typically limited to chat completions and embeddings -- so that exposing them in browser code does not compromise your account.
|
||||
## Creating keys
|
||||
|
||||
```html
|
||||
<!-- Safe to embed in frontend code -->
|
||||
<script>
|
||||
const HANZO_KEY = 'hz-widget-abc123...'
|
||||
</script>
|
||||
```
|
||||
|
||||
## Creating Keys
|
||||
|
||||
### In the Console
|
||||
### In the console
|
||||
|
||||
1. Go to [console.hanzo.ai](https://console.hanzo.ai)
|
||||
2. Select your **project**
|
||||
3. Click **API Keys** in the sidebar
|
||||
4. Click **Create Key**
|
||||
5. Choose the key type (`hk-*` API key or `hz-*` widget key)
|
||||
6. Optionally set:
|
||||
- **Name** -- descriptive label (e.g. `prod-backend`, `staging-widget`)
|
||||
- **Expiration** -- auto-expire after a set duration
|
||||
- **Budget** -- monthly spend limit for LLM usage
|
||||
- **Rate limit** -- requests per minute cap
|
||||
7. Copy the key immediately -- it will not be shown again
|
||||
2. Open **API Keys**
|
||||
3. Create a key and choose its type
|
||||
4. Copy a secret key immediately — it is shown once and never again
|
||||
|
||||
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.
|
||||
A publishable key is the exception: you can read it back at any time, because it
|
||||
is public by construction and useless to you if you cannot.
|
||||
|
||||
## Using Keys
|
||||
### Over the API
|
||||
|
||||
All Hanzo APIs accept keys via the `Authorization` header with the `Bearer` scheme:
|
||||
Keys are one resource, `/v1/keys`, and the type is a field on it. There is no
|
||||
separate mint endpoint and no separate revoke endpoint — the HTTP method says
|
||||
which you mean.
|
||||
|
||||
```
|
||||
Authorization: Bearer hk-proj-abc123...
|
||||
```bash
|
||||
# Mint (or rotate) a key. The response carries it once.
|
||||
curl -X POST https://api.hanzo.ai/v1/keys \
|
||||
-H "Authorization: Bearer sk-your-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"type":"publishable"}'
|
||||
|
||||
# List what you hold. No secret material comes back — a secret key is
|
||||
# represented by its prefix; a publishable key by its full value.
|
||||
curl https://api.hanzo.ai/v1/keys -H "Authorization: Bearer sk-your-key"
|
||||
|
||||
# Revoke one type without touching the other.
|
||||
curl -X DELETE "https://api.hanzo.ai/v1/keys?type=publishable" \
|
||||
-H "Authorization: Bearer sk-your-key"
|
||||
```
|
||||
|
||||
Every service answers on the one host, `api.hanzo.ai`, under `/v1/<service>` — the same key works across all of them:
|
||||
Creating is how you rotate: you hold one key per type, so minting the same type
|
||||
again replaces it and the old credential stops working. Omitting `type` means
|
||||
`secret`, which is what every caller meant before publishable keys existed.
|
||||
|
||||
Two things worth knowing before you automate this. A browser session must echo
|
||||
an anti-CSRF token — `GET /v1/csrf`, then send it back as `X-CSRF-Token` — while
|
||||
a bearer caller does not, because a bearer request cannot be forged by a
|
||||
cross-site page. And revocation reaches IAM immediately but the gateway caches
|
||||
key lookups for a few minutes, so a request that beat the cache may still be
|
||||
served.
|
||||
|
||||
The full request and response shapes are in the [Keys
|
||||
reference](/docs/openapi/keys), generated from the same source as this page.
|
||||
|
||||
## Using keys
|
||||
|
||||
All Hanzo APIs take the key in the `Authorization` header with the `Bearer`
|
||||
scheme:
|
||||
|
||||
```
|
||||
Authorization: Bearer sk-your-key
|
||||
```
|
||||
|
||||
Every service answers on one host, `api.hanzo.ai`, under `/v1/<service>` — the
|
||||
same key works across all of them:
|
||||
|
||||
| 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` |
|
||||
| Secrets | `/v1/kms` | `api.hanzo.ai/v1/kms/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 |
|
||||
| Tasks | `/v1/tasks` | `api.hanzo.ai/v1/tasks/namespaces` |
|
||||
|
||||
### SDK Configuration
|
||||
### SDK configuration
|
||||
|
||||
All official SDKs accept the API key as a constructor parameter:
|
||||
All official SDKs accept the key as a constructor parameter:
|
||||
|
||||
```python
|
||||
# Python
|
||||
from hanzoai import Hanzo
|
||||
client = Hanzo(api_key="hk-your-key")
|
||||
client = Hanzo(api_key="sk-your-key")
|
||||
```
|
||||
|
||||
```typescript
|
||||
// TypeScript
|
||||
import Hanzo from '@hanzo/ai'
|
||||
const client = new Hanzo({ apiKey: 'hk-your-key' })
|
||||
const client = new Hanzo({ apiKey: 'sk-your-key' })
|
||||
```
|
||||
|
||||
```go
|
||||
// Go
|
||||
import "github.com/hanzoai/go-sdk"
|
||||
client := hanzo.NewClient(hanzo.WithAPIKey("hk-your-key"))
|
||||
client := hanzo.NewClient(hanzo.WithAPIKey("sk-your-key"))
|
||||
```
|
||||
|
||||
Each client defaults to `https://api.hanzo.ai/v1`. A third-party HTTP client
|
||||
written against the chat-completions shape works too — point its base URL there
|
||||
and give it the same key.
|
||||
|
||||
## Scopes
|
||||
## Spend and usage
|
||||
|
||||
### Project-Level Keys (`hk-*`, `hz-*`)
|
||||
|
||||
These keys are bound to a single project. They can only access resources within that project. Switching projects requires a different key.
|
||||
|
||||
### Org-Level Keys (`sk-*`)
|
||||
|
||||
These keys operate across all projects within an organization. They are used for administrative operations: managing projects, configuring providers, and accessing org-wide analytics.
|
||||
|
||||
## Key Information and Usage
|
||||
|
||||
Spend and usage for the calling credential come from the billing and analytics APIs — same host, same key:
|
||||
Spend for the calling credential comes from the billing and analytics APIs —
|
||||
same host, same key:
|
||||
|
||||
```bash
|
||||
# Current balance
|
||||
curl https://api.hanzo.ai/v1/billing/balance \
|
||||
-H "Authorization: Bearer hk-your-key"
|
||||
-H "Authorization: Bearer sk-your-key"
|
||||
|
||||
# Itemized metered usage
|
||||
curl https://api.hanzo.ai/v1/billing/usage \
|
||||
-H "Authorization: Bearer hk-your-key"
|
||||
-H "Authorization: Bearer sk-your-key"
|
||||
```
|
||||
|
||||
```json
|
||||
@@ -148,44 +174,72 @@ curl https://api.hanzo.ai/v1/billing/usage \
|
||||
}
|
||||
```
|
||||
|
||||
For rolled-up request/token/spend counts across a range, use `GET /v1/analytics/overview?range=30d` — see [Analytics](/docs/services/analytics).
|
||||
For rolled-up request, token and spend counts across a range, use `GET
|
||||
https://api.hanzo.ai/v1/analytics/overview?range=30d` — see
|
||||
[Analytics](/docs/services/analytics).
|
||||
|
||||
## Rotation Best Practices
|
||||
## Rotation
|
||||
|
||||
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](/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 `/v1/billing/usage` or the console dashboard regularly. Unusual spend patterns may indicate a leak.
|
||||
1. **Rotate on a schedule.** Ninety days is a reasonable default for a secret
|
||||
key. A publishable key is public anyway; rotate it when you want to cut off a
|
||||
site, not on a calendar.
|
||||
2. **Keep production keys in [KMS](/docs/services/kms).** Never in source, never
|
||||
in a committed `.env`.
|
||||
3. **Mint before you revoke.** Mint the replacement, deploy it, watch it work,
|
||||
then revoke. Note that minting the same type again IS the revoke — so for
|
||||
zero-downtime you cut over between two identities, not two keys of one.
|
||||
4. **One identity per service.** Blast radius, and audit logs you can read.
|
||||
5. **Watch spend.** `/v1/billing/usage` or the console. An unfamiliar spend
|
||||
pattern is what a leaked key looks like from the inside.
|
||||
|
||||
### KMS Integration
|
||||
### KMS integration
|
||||
|
||||
For production deployments, use Hanzo KMS to manage API keys as secrets:
|
||||
A `KMSSecret` syncs secrets out of Hanzo KMS into a Kubernetes Secret your pods
|
||||
already read. It authenticates as a machine identity, names a scope in KMS, and
|
||||
names the Secret it manages:
|
||||
|
||||
```yaml
|
||||
# KMSSecret CRD -- syncs a secret from Hanzo KMS into K8s
|
||||
apiVersion: secrets.lux.network/v1alpha1
|
||||
kind: KMSSecret
|
||||
metadata:
|
||||
name: hanzo-api-key
|
||||
name: api-kms-sync
|
||||
namespace: acme
|
||||
spec:
|
||||
secretStoreRef:
|
||||
name: kms-hanzo
|
||||
data:
|
||||
- secretKey: HANZO_API_KEY
|
||||
remoteRef:
|
||||
secretPath: /prod/api-keys
|
||||
secretKey: primary
|
||||
hostAPI: https://kms.hanzo.ai
|
||||
resyncInterval: 60
|
||||
authentication:
|
||||
universalAuth:
|
||||
credentialsRef:
|
||||
secretName: acme-iam-creds
|
||||
secretNamespace: acme
|
||||
secretsScope:
|
||||
projectSlug: acme
|
||||
envSlug: prod
|
||||
secretsPath: /api
|
||||
keys:
|
||||
- HANZO_API_KEY
|
||||
managedSecretReference:
|
||||
creationPolicy: Orphan
|
||||
secretName: api-secrets
|
||||
secretNamespace: acme
|
||||
secretType: Opaque
|
||||
```
|
||||
|
||||
## Revoking Keys
|
||||
`creationPolicy: Orphan` means deleting the CR leaves the Secret in place, so
|
||||
removing the sync does not take a running workload down with it.
|
||||
|
||||
Revoke a key from the console: **Project → API Keys → Revoke**. Revocation is immediate — any in-flight request using the key fails with `401 Unauthorized`.
|
||||
## Revoking
|
||||
|
||||
## Security Rules
|
||||
From the console: **API Keys → Revoke**. Over the API:
|
||||
`DELETE /v1/keys?type=secret`. IAM drops the credential immediately and calls
|
||||
made with it fail `401` once the gateway's cache lapses.
|
||||
|
||||
- **Never commit keys to git.** Use environment variables or KMS.
|
||||
- **Never expose `hk-*` or `sk-*` keys client-side.** Use `hz-*` widget keys for browser code.
|
||||
- **Always use HTTPS.** All Hanzo endpoints enforce TLS. Plain HTTP connections are rejected.
|
||||
- **Set the minimum scope needed.** If a service only needs LLM access, do not give it an org-level secret key.
|
||||
## Rules
|
||||
|
||||
- **Never commit a key.** Environment variables or KMS.
|
||||
- **Never put a secret key in a browser.** It resolves to you. Use a publishable
|
||||
key, which resolves to your org and cannot read.
|
||||
- **Always use HTTPS.** Every Hanzo endpoint enforces TLS; plain HTTP is
|
||||
rejected.
|
||||
- **Ask for the type you need.** An unrecognized type is refused, so a typo
|
||||
fails loudly instead of quietly handing you the more powerful key.
|
||||
|
||||
@@ -29,10 +29,10 @@ Every Hanzo service exposes a REST API under one base URL. One credential works
|
||||
https://api.hanzo.ai/v1
|
||||
```
|
||||
|
||||
Authenticate every request with a bearer token in the `Authorization` header. Use an API key (`hk-...`) for server-to-server calls, or an IAM access token for user-facing apps:
|
||||
Authenticate every request with a bearer token in the `Authorization` header. Use an API key (`sk-...`) for server-to-server calls, or an IAM access token for user-facing apps:
|
||||
|
||||
```
|
||||
Authorization: Bearer hk-...
|
||||
Authorization: Bearer sk-...
|
||||
```
|
||||
|
||||
All traffic is TLS-only and passes through the [Gateway](/docs/gateway), which authenticates, rate-limits, and routes it. See [API Keys](/docs/api-keys) to create a key and [Authentication](/docs/authentication) for OIDC tokens.
|
||||
@@ -52,14 +52,14 @@ The API is versioned at `v1`; it evolves additively, so integrations stay stable
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/models \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
Responses are JSON. Errors use standard HTTP status codes -- `401` for a bad or missing key, `403` when authenticated but forbidden, `429` when rate-limited, `402` when out of credits.
|
||||
|
||||
## Related
|
||||
|
||||
- [API Keys](/docs/api-keys) -- create and manage `hk-` credentials
|
||||
- [API Keys](/docs/api-keys) -- create and manage `sk-` credentials
|
||||
- [Authentication](/docs/authentication) -- OAuth 2.0 + OIDC for user tokens
|
||||
- [Gateway](/docs/gateway) -- the entry point every request passes through
|
||||
- [OpenAPI](/docs/openapi) -- machine-readable specs and generated references
|
||||
|
||||
@@ -40,6 +40,6 @@ Newest builds are always on the [latest release](https://github.com/hanzoai/exte
|
||||
|
||||
## Sign in
|
||||
|
||||
Open the extension and **Sign in with Hanzo**, or paste a `hk-` key in its settings.
|
||||
Open the extension and **Sign in with Hanzo**, or paste a `sk-` key in its settings.
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
@@ -39,4 +39,4 @@ Answer store questions, summarize orders, and draft product copy.
|
||||
|
||||
**Connect** — [console](https://console.hanzo.ai) → **Shopify** → authorize the app for your store.
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
@@ -41,4 +41,4 @@ Draft replies, summarize tickets, and suggest macros from the agent sidebar.
|
||||
|
||||
Newest installers are always on the [latest release](https://github.com/hanzoai/extension/releases/latest).
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
@@ -31,6 +31,6 @@ Newest builds are always on the [latest release](https://github.com/hanzoai/exte
|
||||
|
||||
## Sign in
|
||||
|
||||
Open the plugin and **Sign in with Hanzo**, or paste a `hk-` key in its settings.
|
||||
Open the plugin and **Sign in with Hanzo**, or paste a `sk-` key in its settings.
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
@@ -33,11 +33,11 @@ Add Hanzo's MCP tools to Anthropic's Claude Desktop with a one-file connector.
|
||||
|-------|--------|
|
||||
| [`hanzo-ai-claude-v1.9.31.dxt`](https://github.com/hanzoai/extension/releases/download/v1.9.31/hanzo-ai-claude-v1.9.31.dxt) | Desktop extension (`.dxt`) |
|
||||
|
||||
**Install** — double-click the `.dxt` to open it with Claude Desktop, then confirm the install. Provide your `hk-` key when prompted and the Hanzo tools appear in Claude's tool list.
|
||||
**Install** — double-click the `.dxt` to open it with Claude Desktop, then confirm the install. Provide your `sk-` key when prompted and the Hanzo tools appear in Claude's tool list.
|
||||
|
||||
Newest builds are always on the [latest release](https://github.com/hanzoai/extension/releases/latest).
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ The same review and triage flow for GitLab merge requests and issues.
|
||||
|
||||
**Connect** — from the [console](https://console.hanzo.ai), choose **GitLab**, authorize over OAuth (GitLab.com or self-managed), and pick the groups or projects to enable.
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ Restart JupyterLab; the Hanzo panel appears in the sidebar.
|
||||
|
||||
## Sign in
|
||||
|
||||
Open the Hanzo panel and **Sign in with Hanzo**, or set your `hk-` key in the extension settings (`hanzo.apiKey`).
|
||||
Open the Hanzo panel and **Sign in with Hanzo**, or set your `sk-` key in the extension settings (`hanzo.apiKey`).
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Every surface below is a thin client over the same foundation:
|
||||
You authenticate **once**, either way:
|
||||
|
||||
1. **Sign in with Hanzo** — OAuth through [Hanzo IAM](/docs/iam). Click **Sign in**, approve, done.
|
||||
2. **Paste a `hk-` key** — create one in the [console](https://console.hanzo.ai) (see [API Keys](/docs/api-keys)) and paste it into the surface's settings.
|
||||
2. **Paste a `sk-` key** — create one in the [console](https://console.hanzo.ai) (see [API Keys](/docs/api-keys)) and paste it into the surface's settings.
|
||||
|
||||
The same key and the same identity work in every extension, add-in, and connected app. Switch orgs once and it applies everywhere.
|
||||
|
||||
@@ -51,6 +51,6 @@ Connect these from the [console](https://console.hanzo.ai) or install the app in
|
||||
## Related
|
||||
|
||||
- [Gateway](/docs/gateway) — the one API every surface calls
|
||||
- [API Keys](/docs/api-keys) — mint the `hk-` key you paste in
|
||||
- [API Keys](/docs/api-keys) — mint the `sk-` key you paste in
|
||||
- [IAM](/docs/iam) — the identity you sign in with
|
||||
- [Integrations](/docs/integrations) — connect data sources (blob storage, Slack alerts, analytics) to a project
|
||||
|
||||
@@ -47,4 +47,4 @@ Summarize, query, and extract structured data from PDFs at [pdf.hanzo.ai](https:
|
||||
|
||||
Newest installers are always on the [latest release](https://github.com/hanzoai/extension/releases/latest).
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
@@ -17,6 +17,6 @@ Summarize matters, draft correspondence, and query your practice data inside Cli
|
||||
|
||||
Run Hanzo from the Raycast command bar — ask, draft, and run agents without leaving your keyboard.
|
||||
|
||||
**Install** — open Raycast, search the **Raycast Store** for **Hanzo**, and add the extension. Set your `hk-` key in the extension preferences, or run the **Sign in with Hanzo** command.
|
||||
**Install** — open Raycast, search the **Raycast Store** for **Hanzo**, and add the extension. Set your `sk-` key in the extension preferences, or run the **Sign in with Hanzo** command.
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
@@ -41,4 +41,4 @@ Summarize financials and answer questions about your books.
|
||||
- **Canvas** — education / LMS.
|
||||
- **iManage** — legal document management.
|
||||
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `hk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
> **One identity, everywhere.** Sign in with Hanzo ([IAM](/docs/iam) OAuth) or paste a `sk-` [API key](/docs/api-keys). The same credential works in every Hanzo surface — see [Integrations & Extensions](/docs/apps).
|
||||
|
||||
@@ -27,7 +27,7 @@ A check evaluates a `(subject, resource, action)` request against the active pol
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/authz/check \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"subject": "user:alice",
|
||||
|
||||
@@ -13,7 +13,7 @@ Each instance is an isolated Base backend scoped to your organization, with its
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/base \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "name": "acme-app", "iam": true }'
|
||||
@@ -59,5 +59,5 @@ Each tenant instance is fully isolated, so one organization's data and users nev
|
||||
- [Hanzo Base service reference](/docs/services/base) — collections, hooks, plugins, and SDKs
|
||||
- [SQL](/docs/sql) — the managed PostgreSQL that backs production instances
|
||||
- [Storage](/docs/storage) — S3-compatible storage for uploaded files
|
||||
- [API Keys](/docs/api-keys) — authenticate with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
@@ -16,7 +16,7 @@ Your organization spends from a **cloud-credit balance**. Every billable request
|
||||
```bash
|
||||
# Check your organization's credit balance
|
||||
curl https://api.hanzo.ai/v1/billing/balance \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
```json
|
||||
@@ -45,7 +45,7 @@ Most Hanzo products are **usage-based**: you pay for what you consume -- tokens
|
||||
```bash
|
||||
# Metered usage for the current period, by product and model
|
||||
curl "https://api.hanzo.ai/v1/billing/usage?period=current" \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
Prices are published in the pricing catalog, which the console's Plans & Pricing view reads live:
|
||||
@@ -64,7 +64,7 @@ At the close of each billing cycle, usage and subscription charges are rolled in
|
||||
```bash
|
||||
# List invoices for your organization
|
||||
curl https://api.hanzo.ai/v1/billing/invoices \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
Each invoice lists line items, the period covered, credits applied, and the amount due. The console's Cost view renders the same data with per-product breakdowns.
|
||||
|
||||
@@ -94,7 +94,7 @@ agents:
|
||||
|
||||
```bash
|
||||
# At least one LLM provider key
|
||||
HANZO_API_KEY=hk-your-api-key
|
||||
HANZO_API_KEY=sk-your-api-key
|
||||
|
||||
# Database
|
||||
MONGO_URI=mongodb://localhost:27017/chat
|
||||
|
||||
@@ -35,7 +35,7 @@ MEILI_MASTER_KEY=${SEARCH_MASTER_KEY}
|
||||
|
||||
```bash
|
||||
# Hanzo Gateway (recommended — access all models via one key)
|
||||
HANZO_API_KEY=hk-your-api-key
|
||||
HANZO_API_KEY=sk-your-api-key
|
||||
|
||||
# Direct provider keys (optional — bypass gateway)
|
||||
OPENAI_API_KEY=sk-...
|
||||
|
||||
@@ -66,7 +66,7 @@ Upload files to conversations for context-aware responses using Retrieval-Augmen
|
||||
```bash
|
||||
# Embeddings come from Hanzo. The RAG API's key/base-URL variables are named
|
||||
# for the wire format they speak; point them at api.hanzo.ai and use a Hanzo key.
|
||||
RAG_OPENAI_API_KEY=hk-your-api-key
|
||||
RAG_OPENAI_API_KEY=sk-your-api-key
|
||||
RAG_OPENAI_BASEURL=https://api.hanzo.ai/v1
|
||||
EMBEDDINGS_MODEL=zen-embedding
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ services:
|
||||
|
||||
```bash
|
||||
# Embeddings provider (required)
|
||||
RAG_OPENAI_API_KEY=hk-your-api-key # a Hanzo key
|
||||
RAG_OPENAI_API_KEY=sk-your-api-key # a Hanzo key
|
||||
RAG_OPENAI_BASEURL=https://api.hanzo.ai/v1
|
||||
|
||||
# Embeddings model
|
||||
|
||||
@@ -52,7 +52,7 @@ curl https://api.hanzo.ai/v1/chat/completions \
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen5",
|
||||
|
||||
@@ -27,7 +27,7 @@ The console is a single-page app embedded (`go:embed`) directly into the Hanzo C
|
||||
```bash
|
||||
# The console calls the same API you can call directly, with the same key
|
||||
curl https://api.hanzo.ai/v1/models \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
@@ -13,7 +13,7 @@ Fetch one URL and get back clean markdown (scripts, nav, and boilerplate strippe
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/scrape \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"url": "https://example.com",
|
||||
|
||||
@@ -18,7 +18,7 @@ Every panel is scoped to your organization and reads real metrics. Usage is attr
|
||||
|
||||
## Reading Metrics via the API
|
||||
|
||||
The same numbers a panel renders are readable over the API with an API key (`hk-...`), so a dashboard is never the only place a metric exists. The endpoints and their parameters are in the [API reference](/docs/api).
|
||||
The same numbers a panel renders are readable over the API with an API key (`sk-...`), so a dashboard is never the only place a metric exists. The endpoints and their parameters are in the [API reference](/docs/api).
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ A dataset is a curated collection of **items** — each an input paired with an
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/evals/datasets \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "support-qa",
|
||||
@@ -27,7 +27,7 @@ Items are versioned by dataset, so a run always evaluates against a known snapsh
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/evals/datasets \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
## Run against a model
|
||||
|
||||
@@ -13,7 +13,7 @@ Each datastore is a logical resource scoped to your organization and provisioned
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/datastore \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "name": "events" }'
|
||||
@@ -41,5 +41,5 @@ Datastore is built for append-heavy analytics — events, logs, metrics, and tim
|
||||
- [Analytics engine reference](/docs/services/analytics) — the full SQL surface and table engines
|
||||
- [SQL](/docs/sql) — managed PostgreSQL for transactional data
|
||||
- [Storage](/docs/storage) — S3-compatible object storage for cold data
|
||||
- [API Keys](/docs/api-keys) — authenticate with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
@@ -45,7 +45,7 @@ for k in ["/v1/projects","/v1/git","/v1/code","/v1/sites","/v1/templates","/v1/r
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/projects/fork \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "template": "folio", "slug": "my-folio" }'
|
||||
@@ -84,17 +84,17 @@ Publishing does not upload bytes. The builder's output already lives in Hanzo's
|
||||
```bash
|
||||
# create + activate in one call — the 99% path
|
||||
curl -X POST https://api.hanzo.ai/v1/sites/my-folio/publish \
|
||||
-H "Authorization: Bearer hk-..." -H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Authorization: Bearer sk-..." -H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "source": "builds/my-folio/out" }'
|
||||
|
||||
# the rollback menu
|
||||
curl https://api.hanzo.ai/v1/sites/my-folio/releases \
|
||||
-H "Authorization: Bearer hk-..." -H "X-Org-Id: org_a1b2c3"
|
||||
-H "Authorization: Bearer sk-..." -H "X-Org-Id: org_a1b2c3"
|
||||
|
||||
# rollback is the same flip, aimed at an older id. Nothing is rebuilt.
|
||||
curl -X POST https://api.hanzo.ai/v1/sites/my-folio/releases/rel_<id>/activate \
|
||||
-H "Authorization: Bearer hk-..." -H "X-Org-Id: org_a1b2c3"
|
||||
-H "Authorization: Bearer sk-..." -H "X-Org-Id: org_a1b2c3"
|
||||
```
|
||||
|
||||
`source` is a path **relative to your own org's storage**. The org segment comes from the validated principal and the bucket is server-owned and never appears in the request, so the worst a hostile source string can name is something your org already owns.
|
||||
|
||||
@@ -13,7 +13,7 @@ Each database is a logical resource scoped to your organization and created thro
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/docdb \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "name": "app" }'
|
||||
@@ -58,5 +58,5 @@ DocDB suits flexible, schema-light document data. For relational data and SQL, u
|
||||
- [SQL](/docs/sql) — the managed PostgreSQL that backs DocDB storage
|
||||
- [Vector](/docs/vector) — embeddings and semantic search
|
||||
- [Search](/docs/search) — full-text and hybrid indexes over documents
|
||||
- [API Keys](/docs/api-keys) — authenticate with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
@@ -6,7 +6,7 @@ description: Generate, store, and search vector embeddings at scale — the embe
|
||||
|
||||
# Hanzo Embeddings
|
||||
|
||||
Hanzo Embeddings turns text into vectors on the AI API — one endpoint, the same `hk-` key as everything else. Generate embeddings, then store and query them in [Vector](/docs/vector) for semantic search and RAG.
|
||||
Hanzo Embeddings turns text into vectors on the AI API — one endpoint, the same `sk-` key as everything else. Generate embeddings, then store and query them in [Vector](/docs/vector) for semantic search and RAG.
|
||||
|
||||
## Generate Embeddings
|
||||
|
||||
@@ -14,7 +14,7 @@ Hanzo Embeddings turns text into vectors on the AI API — one endpoint, the sam
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/embeddings \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "zen-embedding",
|
||||
@@ -42,7 +42,7 @@ Provision a [Vector](/docs/vector) collection whose `dimensions` match your mode
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-...")
|
||||
client = Hanzo(api_key="sk-...")
|
||||
|
||||
vecs = client.embeddings.create(
|
||||
model="zen-embedding",
|
||||
@@ -59,7 +59,7 @@ vecs = client.embeddings.create(
|
||||
|
||||
The catalogue also carries open-weight embedding models such as `bge-m3`. `GET /v1/models` lists every id this endpoint accepts — see [Models](/docs/models). Whichever you pick, set your Vector collection's `dimensions` from the length of the vector you actually get back rather than assuming it.
|
||||
|
||||
Pricing follows the AI API. Because embeddings share the gateway's per-key budgets and rate limits, the same `hk-` key governs spend across chat and embeddings.
|
||||
Pricing follows the AI API. Because embeddings share the gateway's per-key budgets and rate limits, the same `sk-` key governs spend across chat and embeddings.
|
||||
|
||||
`/v1/embeddings` takes and returns the same JSON shapes as the widely-implemented embeddings format, so an HTTP client written against it works here once its base URL points at `https://api.hanzo.ai/v1` and it sends a Hanzo key.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ An experiment runs a [dataset](/docs/datasets) against a model — optionally wi
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/evals/runs \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"dataset": "support-qa",
|
||||
|
||||
@@ -87,7 +87,7 @@ Mint a key at [console.hanzo.ai](https://console.hanzo.ai) (see
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/chat/completions \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "enso",
|
||||
@@ -103,7 +103,7 @@ Write the response to `snake.html` and open it. The same thing in Python:
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-...")
|
||||
client = Hanzo(api_key="sk-...")
|
||||
|
||||
game = client.chat.completions.create(
|
||||
model="enso",
|
||||
|
||||
@@ -15,7 +15,7 @@ The Hanzo Gateway is the single front door to the platform. Every request to `ap
|
||||
|
||||
"Gated" and "priced" are the gateway's job:
|
||||
|
||||
- **Gated** -- the gateway rejects unauthenticated or over-quota requests before they reach a backend. Present an API key (`hk-...`) or an IAM bearer token. Rate limits and circuit breakers protect every downstream service.
|
||||
- **Gated** -- the gateway rejects unauthenticated or over-quota requests before they reach a backend. Present an API key (`sk-...`) or an IAM bearer token. Rate limits and circuit breakers protect every downstream service.
|
||||
- **Priced** -- metered usage is attributed to your organization and priced by the commerce subsystem. See [Billing](/docs/billing) for credits, metering, and invoices.
|
||||
|
||||
The gateway forwards your `Authorization` header unchanged and streams responses (SSE) without transformation, so an HTTP client written against the chat-completions shape works against it once its base URL points here.
|
||||
@@ -36,7 +36,7 @@ All services are reached under a single base -- `https://api.hanzo.ai/v1` -- nam
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/chat/completions \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "zen5",
|
||||
|
||||
@@ -55,20 +55,22 @@ All resources -- API keys, models, usage logs -- are scoped to the active projec
|
||||
Your key will look like this:
|
||||
|
||||
```
|
||||
hk-proj-abc123def456...
|
||||
sk-abc123def456...
|
||||
```
|
||||
|
||||
The `hk-` prefix identifies it as a Hanzo API key. See [API Keys](/docs/api-keys) for a full breakdown of key types and prefixes.
|
||||
The `sk-` prefix says this one is secret: it resolves to you, so it belongs on a
|
||||
server. The other prefix, `pk-`, resolves only to your organization and is the
|
||||
one you may put in a browser bundle. See [API Keys](/docs/api-keys).
|
||||
|
||||
## 5. Make Your First API Call
|
||||
|
||||
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.
|
||||
Every Hanzo service — AI, storage, identity, deploys — answers on **one host, `api.hanzo.ai`**, under `/v1/<service>`, with the same `sk-*` key. There is no second API host to configure.
|
||||
|
||||
### curl
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/chat/completions \
|
||||
-H "Authorization: Bearer hk-your-api-key" \
|
||||
-H "Authorization: Bearer sk-your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "zen5",
|
||||
@@ -80,7 +82,7 @@ List every model id the same way — `/v1/models` takes the same key:
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/models \
|
||||
-H "Authorization: Bearer hk-your-api-key"
|
||||
-H "Authorization: Bearer sk-your-api-key"
|
||||
```
|
||||
|
||||
### Python
|
||||
@@ -94,7 +96,7 @@ pip install hanzoai
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen5",
|
||||
@@ -114,7 +116,7 @@ npm install @hanzo/ai
|
||||
```typescript
|
||||
import Hanzo from '@hanzo/ai'
|
||||
|
||||
const client = new Hanzo({ apiKey: 'hk-your-api-key' })
|
||||
const client = new Hanzo({ apiKey: 'sk-your-api-key' })
|
||||
|
||||
const completion = await client.chat.completions.create({
|
||||
model: 'zen5',
|
||||
@@ -146,13 +148,14 @@ Building a funded startup? You may qualify for **up to $150,000 in credits** thr
|
||||
|
||||
## Key Types at a Glance
|
||||
|
||||
| Prefix | Type | Purpose |
|
||||
|--------|------|---------|
|
||||
| `hk-*` | API Key | Server-side API access. Scoped to a project. |
|
||||
| `sk-*` | Secret Key | Provider-level keys (e.g. upstream model providers). Never expose client-side. |
|
||||
| `hz-*` | Widget Key | Client-side keys for browser widgets. Restricted scopes, safe to embed. |
|
||||
| Prefix | Type | Resolves to | Where it belongs |
|
||||
|--------|------|-------------|------------------|
|
||||
| `sk-*` | Secret | You | A server. It authenticates as you, so it can do anything you can. |
|
||||
| `pk-*` | Publishable | Your org | Anywhere, browser bundles included. It identifies, it does not authenticate. |
|
||||
|
||||
See [API Keys](/docs/api-keys) for details on scopes, rotation, and production best practices.
|
||||
Two types, and that is the whole list — a key spelled any other way is refused at
|
||||
the door. See [API Keys](/docs/api-keys) for what each one can do, how to rotate
|
||||
them, and how to keep them out of your source tree.
|
||||
|
||||
## What Next?
|
||||
|
||||
|
||||
@@ -42,12 +42,12 @@ See [Authentication](/docs/authentication) for the full OAuth 2.0 + PKCE flow, t
|
||||
|
||||
## Managing Identity via the API
|
||||
|
||||
Identity resources are served under `/v1/iam` on the gateway. Authenticate with an API key (`hk-...`) or an IAM bearer token; requests are scoped to the caller's organization.
|
||||
Identity resources are served under `/v1/iam` on the gateway. Authenticate with an API key (`sk-...`) or an IAM bearer token; requests are scoped to the caller's organization.
|
||||
|
||||
```bash
|
||||
# List users in your organization
|
||||
curl https://api.hanzo.ai/v1/iam/users \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
Every request shares the same shape -- `https://api.hanzo.ai/v1/<service>/...` with a bearer token. For the complete IAM resource reference (organizations, applications, roles, permissions, providers), see [Hanzo IAM](/docs/services/iam) and the [API reference](/docs/api).
|
||||
|
||||
@@ -26,7 +26,7 @@ List and manage a project's connections over the API. The exact fields vary per
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/integrations \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
```json
|
||||
|
||||
@@ -21,12 +21,12 @@ KMS is zero-knowledge by design: values are encrypted at rest, access is governe
|
||||
|
||||
## Secrets and Cryptographic Operations
|
||||
|
||||
Secrets and key operations are served under `/v1/kms` on the gateway, or directly at `kms.hanzo.ai`. Authenticate with an API key (`hk-...`).
|
||||
Secrets and key operations are served under `/v1/kms` on the gateway, or directly at `kms.hanzo.ai`. Authenticate with an API key (`sk-...`).
|
||||
|
||||
```bash
|
||||
# Read a secret from an environment path
|
||||
curl "https://api.hanzo.ai/v1/kms/secrets?environment=production&path=/api-keys" \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
Keys never leave the boundary. For signing and threshold operations, KMS delegates to MPC and returns only the signature -- the private key material is split across independent nodes and never reconstructed.
|
||||
@@ -39,20 +39,34 @@ For workloads on Kubernetes, the KMS operator syncs secrets into the cluster wit
|
||||
apiVersion: secrets.lux.network/v1alpha1
|
||||
kind: KMSSecret
|
||||
metadata:
|
||||
name: hanzo-api-key
|
||||
name: api-kms-sync
|
||||
namespace: acme
|
||||
spec:
|
||||
secretStoreRef:
|
||||
name: kms-hanzo
|
||||
data:
|
||||
- secretKey: HANZO_API_KEY
|
||||
remoteRef:
|
||||
secretPath: /prod/api-keys
|
||||
secretKey: primary
|
||||
hostAPI: https://kms.hanzo.ai
|
||||
resyncInterval: 60
|
||||
authentication:
|
||||
universalAuth:
|
||||
credentialsRef:
|
||||
secretName: acme-iam-creds
|
||||
secretNamespace: acme
|
||||
secretsScope:
|
||||
projectSlug: acme
|
||||
envSlug: prod
|
||||
secretsPath: /api-keys
|
||||
keys:
|
||||
- HANZO_API_KEY
|
||||
managedSecretReference:
|
||||
creationPolicy: Orphan
|
||||
secretName: api-secrets
|
||||
secretNamespace: acme
|
||||
secretType: Opaque
|
||||
```
|
||||
|
||||
Every field is in the [KMSSecret reference](/docs/services/kms/kmssecret-crd).
|
||||
|
||||
## Related
|
||||
|
||||
- [API Keys](/docs/api-keys) -- store and rotate `hk-` keys in KMS for production
|
||||
- [API Keys](/docs/api-keys) -- store and rotate `sk-` keys in KMS for production
|
||||
- [MPC](/docs/mpc) -- threshold signing backend for KMS-managed keys
|
||||
- [IAM](/docs/iam) -- identity that authorizes KMS access
|
||||
- [Zero Trust](/docs/zero-trust) -- private access to services that consume secrets
|
||||
|
||||
@@ -13,7 +13,7 @@ An instance is a logical namespace in the shared Hanzo KV backend, scoped to you
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/kv \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "name": "cache", "eviction": "allkeys-lru" }'
|
||||
@@ -54,5 +54,5 @@ Rich data structures (hashes, sets, sorted sets, bitmaps, HyperLogLog, geospatia
|
||||
- [Hanzo KV service reference](/docs/services/kv) — the full command set, ACLs, and clustering
|
||||
- [PubSub](/docs/pubsub) — durable, at-least-once messaging with JetStream
|
||||
- [SQL](/docs/sql) — managed PostgreSQL for persistent relational data
|
||||
- [API Keys](/docs/api-keys) — authenticate with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Sparkles } from 'lucide-react'
|
||||
Hanzo Cloud, and know which model id to put in it.**
|
||||
|
||||
The AI API is where Hanzo Cloud runs inference. It sits on the same host as
|
||||
every other Hanzo service — `api.hanzo.ai` — behind the same `hk-*` key. An
|
||||
every other Hanzo service — `api.hanzo.ai` — behind the same `sk-*` key. An
|
||||
application that already calls Hanzo for storage or search does not pick up a
|
||||
second base URL or a second credential to start calling models.
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Emit logs through the OTLP endpoint, or write structured JSON to stdout and let
|
||||
|
||||
```bash
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.hanzo.ai/v1
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer hk-...,X-Org-Id=org_123"
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer sk-...,X-Org-Id=org_123"
|
||||
export OTEL_SERVICE_NAME=checkout-api
|
||||
```
|
||||
|
||||
@@ -25,7 +25,7 @@ Filter by service, level, and time window. The list response is the standard `{
|
||||
|
||||
```bash
|
||||
curl "https://api.hanzo.ai/v1/logs?service=checkout-api&level=error&limit=100" \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
```json
|
||||
|
||||
@@ -17,7 +17,7 @@ The storefront is backed by the pricing catalog, which you can query directly:
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/pricing/models \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
```json
|
||||
@@ -39,11 +39,11 @@ Prices are the real per-million-token rates; a model with no published price ren
|
||||
|
||||
## Deploy a Model
|
||||
|
||||
Once you have picked a model, call it through the [AI API](/docs/llm) with the same `hk-` key:
|
||||
Once you have picked a model, call it through the [AI API](/docs/llm) with the same `sk-` key:
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/chat/completions \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "zen5",
|
||||
|
||||
@@ -23,5 +23,5 @@ Memory builds on the same embedding and vector primitives as the rest of the pla
|
||||
- [Embeddings](/docs/embeddings) — the vectors behind semantic recall
|
||||
- [Vector](/docs/vector) — the managed vector database for custom RAG
|
||||
- [MCP](/docs/mcp) — expose memory to agents as a tool
|
||||
- [API Keys](/docs/api-keys) — authenticate with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
@@ -9,7 +9,7 @@ Metrics are the numeric time series behind your product: request rates, token th
|
||||
|
||||
## Emitting metrics
|
||||
|
||||
Push metrics through the OTLP endpoint on the gateway, or expose a Prometheus `/metrics` endpoint and let O11y scrape it. Either way the same `hk-*` key authenticates and `X-Org-Id` scopes the series.
|
||||
Push metrics through the OTLP endpoint on the gateway, or expose a Prometheus `/metrics` endpoint and let O11y scrape it. Either way the same `sk-*` key authenticates and `X-Org-Id` scopes the series.
|
||||
|
||||
```python
|
||||
from prometheus_client import Counter, Histogram
|
||||
@@ -30,7 +30,7 @@ For OTLP, point your exporter at the gateway — metrics land at `POST /v1/metri
|
||||
|
||||
```bash
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.hanzo.ai/v1
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer hk-...,X-Org-Id=org_123"
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer sk-...,X-Org-Id=org_123"
|
||||
```
|
||||
|
||||
## Querying with PromQL
|
||||
@@ -39,7 +39,7 @@ Query stored series with PromQL over REST:
|
||||
|
||||
```bash
|
||||
curl "https://api.hanzo.ai/v1/metrics/query" \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
--data-urlencode 'query=rate(inference_requests_total[5m])'
|
||||
```
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ Every resource in Hanzo is scoped to exactly one organization:
|
||||
| Resource | Scoped To |
|
||||
|----------|-----------|
|
||||
| **Projects** | Organization |
|
||||
| **API Keys** (`hk-*`, `hz-*`) | Project (within an org) |
|
||||
| **Secret Keys** (`sk-*`) | Organization |
|
||||
| **Secret keys** (`sk-*`) | The member who minted it — one per user |
|
||||
| **Publishable keys** (`pk-*`) | Organization, or the project it was minted with |
|
||||
| **Usage & Billing** | Organization |
|
||||
| **Team Members** | Organization |
|
||||
| **KMS Secrets** | Organization + project |
|
||||
@@ -89,9 +89,12 @@ For more on tasks, see [Hanzo Tasks](/docs/tasks).
|
||||
|
||||
When using OIDC tokens (from OAuth login), the organization is embedded in the JWT `owner` claim. The API gateway reads this claim and scopes all requests to that organization.
|
||||
|
||||
When using API keys (`hk-*`), the organization is determined by the project the key belongs to. No additional header is needed.
|
||||
A secret key (`sk-*`) resolves to the member who minted it, so requests scope to
|
||||
that member's organization. A publishable key (`pk-*`) resolves to the
|
||||
organization itself — or to the project it was minted with. Either way no
|
||||
additional header is needed.
|
||||
|
||||
For administrative operations that span an organization (e.g. listing all projects), use an org-level secret key (`sk-*`):
|
||||
For administrative operations that span an organization (e.g. listing all projects), use a secret key (`sk-*`) held by a member with the rights to do it:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer sk-org-abc123..." \
|
||||
|
||||
@@ -14,7 +14,7 @@ Prompt management with immutable versions: every edit creates a new version, and
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/prompts \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "support-reply",
|
||||
@@ -32,11 +32,11 @@ List prompt metadata, or fetch one by name to get its full version history. Beca
|
||||
```bash
|
||||
# List prompts (name, versions, labels, tags)
|
||||
curl https://api.hanzo.ai/v1/prompts \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
|
||||
# One prompt, with version history
|
||||
curl https://api.hanzo.ai/v1/prompts/support-reply \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
```json
|
||||
|
||||
@@ -12,7 +12,7 @@ Hanzo Registry is a unified, OCI-compliant registry for container images and mod
|
||||
Log in with your Hanzo API key, then push and pull with Docker, Podman, Buildah, or any OCI client.
|
||||
|
||||
```bash
|
||||
# Log in (username `hanzo`, password is your hk- key)
|
||||
# Log in (username `hanzo`, password is your sk- key)
|
||||
echo "$HANZO_API_KEY" | docker login registry.hanzo.ai -u hanzo --password-stdin
|
||||
|
||||
# Tag and push
|
||||
@@ -40,5 +40,5 @@ Blobs are stored on [Storage](/docs/storage) (Hanzo S3), so the registry inherit
|
||||
|
||||
- [Hanzo Registry service reference](/docs/services/registry) — tagging, model registry, and CI/CD
|
||||
- [Storage](/docs/storage) — the S3-compatible backend for registry blobs
|
||||
- [API Keys](/docs/api-keys) — authenticate `docker login` with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate `docker login` with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
@@ -51,16 +51,16 @@ See [Organizations](/docs/organizations) for the full role matrix.
|
||||
From the console, **Projects** → **New Project** → **API Keys** → **Create Key**.
|
||||
|
||||
```
|
||||
hk-proj-org-startx-abc123...
|
||||
sk-abc123...
|
||||
```
|
||||
|
||||
Copy it now. Set it in your shell:
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY="hk-proj-org-startx-abc123..."
|
||||
export HANZO_API_KEY="sk-abc123..."
|
||||
```
|
||||
|
||||
This single key is the credential for every Hanzo service. The gateway routes calls based on the key's project scope.
|
||||
This single key is the credential for every Hanzo service. A secret key resolves to the member who minted it, so calls carry that member's identity and their organization.
|
||||
|
||||
## 4. Seed secrets into KMS
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
Set your key:
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY="hk-..." # from console.hanzo.ai
|
||||
export HANZO_API_KEY="sk-..." # from console.hanzo.ai
|
||||
```
|
||||
|
||||
## Quick start
|
||||
@@ -44,7 +44,7 @@ import (
|
||||
|
||||
func main() {
|
||||
client := hanzoai.NewClient(
|
||||
option.WithAPIKey("hk-your-key"), // or HANZO_API_KEY
|
||||
option.WithAPIKey("sk-your-key"), // or HANZO_API_KEY
|
||||
)
|
||||
|
||||
resp, err := client.Chat.Completions.New(context.TODO(), hanzoai.ChatCompletionNewParams{
|
||||
|
||||
@@ -113,7 +113,7 @@ resource:
|
||||
```python tab="Python"
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-key")
|
||||
client = Hanzo(api_key="sk-your-key")
|
||||
response = client.chat.completions.create(
|
||||
model="zen5",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
@@ -124,7 +124,7 @@ print(response.choices[0].message.content)
|
||||
```typescript tab="TypeScript"
|
||||
import Hanzo from '@hanzo/sdk';
|
||||
|
||||
const client = new Hanzo({ apiKey: 'hk-your-key' });
|
||||
const client = new Hanzo({ apiKey: 'sk-your-key' });
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'zen5',
|
||||
messages: [{ role: 'user', content: 'Hello' }],
|
||||
@@ -132,7 +132,7 @@ const response = await client.chat.completions.create({
|
||||
```
|
||||
|
||||
```go tab="Go"
|
||||
client := hanzoai.NewClient(option.WithAPIKey("hk-your-key"))
|
||||
client := hanzoai.NewClient(option.WithAPIKey("sk-your-key"))
|
||||
response, _ := client.Chat.Completions.New(ctx, hanzoai.ChatCompletionNewParams{
|
||||
Model: hanzoai.F("zen5"),
|
||||
Messages: hanzoai.F([]hanzoai.ChatCompletionMessageParamUnion{
|
||||
@@ -144,7 +144,7 @@ response, _ := client.Chat.Completions.New(ctx, hanzoai.ChatCompletionNewParams{
|
||||
## One key, one base URL
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY="hk-..." # from console.hanzo.ai
|
||||
export HANZO_API_KEY="sk-..." # from console.hanzo.ai
|
||||
# Base URL for every service: https://api.hanzo.ai/v1
|
||||
```
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ pip install hanzoai
|
||||
Both are generally available on PyPI. Set your key once:
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY="hk-..." # from console.hanzo.ai
|
||||
export HANZO_API_KEY="sk-..." # from console.hanzo.ai
|
||||
```
|
||||
|
||||
## Quick start — Cloud SDK
|
||||
@@ -46,7 +46,7 @@ for chat:
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-key") # or reads HANZO_API_KEY
|
||||
client = Hanzo(api_key="sk-your-key") # or reads HANZO_API_KEY
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen5",
|
||||
@@ -75,7 +75,7 @@ import asyncio
|
||||
from hanzoai import AsyncHanzo
|
||||
|
||||
async def main():
|
||||
client = AsyncHanzo(api_key="hk-your-key")
|
||||
client = AsyncHanzo(api_key="sk-your-key")
|
||||
response = await client.chat.completions.create(
|
||||
model="zen5",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
@@ -131,7 +131,7 @@ response = client.chat.completions.create(
|
||||
|
||||
```python
|
||||
client = Hanzo(
|
||||
api_key="hk-your-key",
|
||||
api_key="sk-your-key",
|
||||
base_url="https://api.hanzo.ai/v1", # default
|
||||
timeout=60.0,
|
||||
max_retries=2,
|
||||
|
||||
@@ -35,7 +35,7 @@ tokio = { version = "1", features = ["full"] }
|
||||
Set your key:
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY="hk-..." # from console.hanzo.ai
|
||||
export HANZO_API_KEY="sk-..." # from console.hanzo.ai
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
@@ -43,7 +43,7 @@ bun add @hanzo/sdk
|
||||
Set your key once:
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY="hk-..." # from console.hanzo.ai
|
||||
export HANZO_API_KEY="sk-..." # from console.hanzo.ai
|
||||
```
|
||||
|
||||
## Quick start — Cloud SDK
|
||||
|
||||
@@ -13,7 +13,7 @@ An index is a logical resource scoped to your organization. It is created on fir
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/search/indexes \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "uid": "products", "primaryKey": "id" }'
|
||||
@@ -40,5 +40,5 @@ Configure an embedder on the index, then blend keyword and semantic ranking with
|
||||
- [Hanzo Search service reference](/docs/services/search) — the full indexing, settings, and chat API
|
||||
- [Vector](/docs/vector) — dense-only similarity search for RAG pipelines
|
||||
- [Embeddings](/docs/embeddings) — generate the vectors that power hybrid search
|
||||
- [API Keys](/docs/api-keys) — authenticate with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
@@ -215,7 +215,7 @@ const agent = new Agent({
|
||||
|
||||
## IAM and KMS
|
||||
|
||||
Agents authenticate to the control plane with the same `hk-*` API key as the rest of the stack. The control plane:
|
||||
Agents authenticate to the control plane with the same `sk-*` API key as the rest of the stack. The control plane:
|
||||
|
||||
- Calls [Hanzo IAM](/docs/services/iam) to validate the token and resolve the org.
|
||||
- Reads any per-agent secrets from [Hanzo KMS](/docs/services/kms) (model API keys, tool credentials).
|
||||
|
||||
@@ -239,7 +239,7 @@ agents:
|
||||
|
||||
```bash
|
||||
# Required
|
||||
HANZO_API_KEY=hk-your-api-key
|
||||
HANZO_API_KEY=sk-your-api-key
|
||||
|
||||
# Database
|
||||
MONGO_URI=mongodb://localhost:27017/chat
|
||||
|
||||
@@ -16,7 +16,7 @@ Hanzo Cloud is the core AI Cloud OS that powers `api.hanzo.ai`. It provides a un
|
||||
- **Knowledge Base**: RAG-powered document stores with vector embeddings
|
||||
- **Container Cloud**: Kubernetes application templates and management
|
||||
- **Outside Providers**: Connect Anthropic, OpenAI, Fireworks or DO-AI when a workflow needs a specific model
|
||||
- **IAM Authentication**: OAuth2/OIDC via Hanzo IAM, API keys (`hk-*`), widget keys (`hz-*`)
|
||||
- **IAM Authentication**: OAuth2/OIDC via Hanzo IAM, secret keys (`sk-*`), publishable keys (`pk-*`)
|
||||
- **Usage Billing**: Per-request metering via Hanzo Commerce API
|
||||
- **Rate Limiting**: Dynamic tier-based limits (free → enterprise)
|
||||
- **Node Management**: Remote infrastructure via VNC/RDP
|
||||
@@ -66,7 +66,7 @@ Create an API key at [console.hanzo.ai](https://console.hanzo.ai).
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/chat/completions \
|
||||
-H "Authorization: Bearer hk-your-api-key" \
|
||||
-H "Authorization: Bearer sk-your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "zen5-flash",
|
||||
@@ -79,7 +79,7 @@ curl https://api.hanzo.ai/v1/chat/completions \
|
||||
```typescript
|
||||
import Hanzo from '@hanzo/ai'
|
||||
|
||||
const client = new Hanzo({ apiKey: 'hk-your-api-key' })
|
||||
const client = new Hanzo({ apiKey: 'sk-your-api-key' })
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'zen5-flash',
|
||||
@@ -101,20 +101,20 @@ Hanzo Cloud exposes three protocols for accessing AI services:
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/chat/completions \
|
||||
-H "Authorization: Bearer hk-your-key" \
|
||||
-H "Authorization: Bearer sk-your-key" \
|
||||
-d '{"model": "zen5-flash", "messages": [{"role": "user", "content": "Hello"}]}'
|
||||
```
|
||||
|
||||
`/v1/chat/completions`, `/v1/embeddings` and `/v1/models` take and return the
|
||||
chat-completions JSON shape, so an HTTP client already written against it works
|
||||
here once its base URL points at `https://api.hanzo.ai/v1` and it sends an
|
||||
`hk-*` key. Model ids do not carry across vendors — `zen5` and `enso` are Hanzo
|
||||
`sk-*` key. Model ids do not carry across vendors — `zen5` and `enso` are Hanzo
|
||||
models and resolve only here.
|
||||
|
||||
### MCP (Model Context Protocol)
|
||||
|
||||
```bash
|
||||
npx @hanzo/mcp --api-key hk-your-key
|
||||
npx @hanzo/mcp --api-key sk-your-key
|
||||
```
|
||||
|
||||
Or configure in your AI client's MCP settings:
|
||||
@@ -124,7 +124,7 @@ Or configure in your AI client's MCP settings:
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "npx",
|
||||
"args": ["@hanzo/mcp", "--api-key", "hk-your-key"]
|
||||
"args": ["@hanzo/mcp", "--api-key", "sk-your-key"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,10 +147,9 @@ Hanzo Cloud supports multiple auth methods:
|
||||
|
||||
| Method | Format | Use Case |
|
||||
|--------|--------|----------|
|
||||
| IAM API Key | `hk-*` | Server-side API access |
|
||||
| Secret key | `sk-*` | Server-side API access, as the user who minted it |
|
||||
| Publishable key | `pk-*` | Browser bundles — analytics, product insights, error capture |
|
||||
| IAM JWT | Bearer token | User sessions via hanzo.id |
|
||||
| Provider Key | `sk-*` | Direct provider passthrough |
|
||||
| Widget Key | `hz-*` | Public-facing chat widgets |
|
||||
|
||||
All auth flows integrate with [Hanzo IAM](/docs/services/iam) for identity management.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ Ingestion: Your App ──► SDK/HTTP ──► api.hanzo.ai/v1/console/* ─
|
||||
|
||||
### 1. Get an API Key
|
||||
|
||||
Sign in at `console.hanzo.ai` with Hanzo IAM SSO. Navigate to **Settings > API Keys** and create a new key. Keys are scoped to a project and prefixed with `hk-`.
|
||||
Sign in at `console.hanzo.ai` with Hanzo IAM SSO. Navigate to **Settings > API Keys** and create a key. Take a secret key (`sk-`) for server-side tracing; take a publishable key (`pk-`) for the browser SDK, which resolves only to your org and so is safe to ship.
|
||||
|
||||
### 2. Send a Trace
|
||||
|
||||
@@ -90,7 +90,7 @@ Open `console.hanzo.ai`, select your project, and click **Traces**. Your trace a
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key", trace=True)
|
||||
client = Hanzo(api_key="sk-your-api-key", trace=True)
|
||||
|
||||
# Every call is traced automatically
|
||||
response = client.chat.completions.create(
|
||||
@@ -139,7 +139,7 @@ response = client.chat.completions.create(
|
||||
```typescript
|
||||
import Hanzo from '@hanzo/ai'
|
||||
|
||||
const client = new Hanzo({ apiKey: 'hk-your-api-key', trace: true })
|
||||
const client = new Hanzo({ apiKey: 'sk-your-api-key', trace: true })
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'zen5',
|
||||
@@ -153,7 +153,7 @@ const response = await client.chat.completions.create({
|
||||
```typescript
|
||||
import { ConsoleClient } from '@hanzo/ai/console'
|
||||
|
||||
const console = new ConsoleClient({ publicKey: 'hk-your-api-key' })
|
||||
const console = new ConsoleClient({ publicKey: 'pk-your-key' })
|
||||
const trace = console.trace({ name: 'rag-pipeline' })
|
||||
|
||||
const retrieval = trace.span({ name: 'retrieval', input: { query } })
|
||||
|
||||
@@ -432,21 +432,34 @@ kubectl create secret generic mpc-encryption-keys \
|
||||
For production, store encryption keys in Hanzo KMS and sync via the KMS operator:
|
||||
|
||||
```yaml
|
||||
apiVersion: secrets.hanzo.ai/v1
|
||||
apiVersion: secrets.lux.network/v1alpha1
|
||||
kind: KMSSecret
|
||||
metadata:
|
||||
name: mpc-encryption-keys
|
||||
name: mpc-kms-sync
|
||||
namespace: mpc
|
||||
spec:
|
||||
secretRef:
|
||||
hostAPI: https://kms.hanzo.ai
|
||||
resyncInterval: 60
|
||||
authentication:
|
||||
universalAuth:
|
||||
credentialsRef:
|
||||
secretName: mpc-iam-creds
|
||||
secretNamespace: mpc
|
||||
secretsScope:
|
||||
projectSlug: hanzo
|
||||
envSlug: prod
|
||||
secretsPath: /mpc/encryption
|
||||
keys:
|
||||
- NODE_ENCRYPTION_KEY
|
||||
managedSecretReference:
|
||||
creationPolicy: Orphan
|
||||
secretName: mpc-encryption-keys
|
||||
data:
|
||||
- secretKey: node-key
|
||||
remoteRef:
|
||||
secretPath: /mpc/encryption
|
||||
secretKey: NODE_ENCRYPTION_KEY
|
||||
secretNamespace: mpc
|
||||
secretType: Opaque
|
||||
```
|
||||
|
||||
Every field is in the [KMSSecret reference](/docs/services/kms/kmssecret-crd).
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
@@ -13,7 +13,7 @@ Hanzo Gateway is a **high-performance API gateway** that routes every Hanzo and
|
||||
- **OpenAI-compatible**: `/v1/chat/completions`, `/v1/models`, etc. work with any OpenAI SDK
|
||||
- **Host-based routing**: Transparent reverse proxy for 40+ Hanzo domains (hanzo.id, chat.hanzo.ai, platform.hanzo.ai, etc.)
|
||||
- **JWT + billing middleware**: Validates IAM tokens via JWKS, checks Commerce billing balance, injects identity headers
|
||||
- **Widget security**: Per-IP rate limiting and origin validation for public widget keys (`hz_*`)
|
||||
- **Publishable-key security**: Per-IP rate limiting and origin validation for the browser key (`pk-*`)
|
||||
- **ZAP binary transport**: TLS 1.3+PQ (post-quantum) listener for native binary protocol backends via `luxfi/zap`
|
||||
- **Plugin system**: Server, client, and modifier plugins loaded at runtime
|
||||
|
||||
@@ -184,9 +184,9 @@ make logs-lux
|
||||
|
||||
Three gin middlewares run in order before any request reaches a backend:
|
||||
|
||||
1. **Auth middleware** (`auth_middleware.go`): Validates JWT via JWKS, checks billing via Commerce API (fail-open), injects `X-IAM-Org-Id`, `X-IAM-User-Id`, `X-IAM-User-Email` headers. API keys (`hk-*`, `sk-*`, `fw_*`, `hz_*`, `pk-*`) pass through without JWT validation.
|
||||
1. **Auth middleware** (`auth_middleware.go`): Validates JWT via JWKS, checks billing via Commerce API (fail-open), injects `X-IAM-Org-Id`, `X-IAM-User-Id`, `X-IAM-User-Email` headers. An opaque API key — `sk-*` or `pk-*`, the two spellings the door recognizes — is resolved against IAM rather than parsed as a JWT.
|
||||
|
||||
2. **Widget security** (`widget_security.go`): Per-IP rate limiting (10 req/min default) and origin validation for widget keys (`hz_*`). Prevents abuse of public-facing widget credentials.
|
||||
2. **Publishable-key security** (`widget_security.go`): Per-IP rate limiting (10 req/min default) and origin validation for `pk-*` keys. A publishable key ships in a browser bundle by design, so the origin is the only thing left to check.
|
||||
|
||||
3. **Host proxy** (`hostProxyMiddleware` in `legacy_transports.go`, reading the table `routes.go` compiles): Routes 40+ Hanzo domains to their backend K8s services via `httputil.ReverseProxy`. Supports WebSocket upgrades natively. Path-based routing for multi-backend hosts (e.g., `platform.hanzo.ai` routes `/v1/` to the platform API, `/` to paas-ui).
|
||||
|
||||
@@ -392,8 +392,8 @@ sd.go # Service discovery registration
|
||||
| 429 Too Many Requests | Global or per-IP rate limit exceeded | Check `qos/ratelimit/router` in gateway.json; increase limits or add per-endpoint overrides |
|
||||
| 401 Unauthorized (JWT) | Invalid or expired JWT token | Verify token against `https://hanzo.id/.well-known/jwks`; check `AUTH_ISSUER` matches |
|
||||
| 402 Payment Required | User has zero or negative Commerce balance | Add funds; or disable billing with `BILLING_ENABLED=false` |
|
||||
| 403 Forbidden (widget) | Widget key (`hz_*`) used from unauthorized origin | Add origin to `AllowedOrigins` in `widget_security.go`; or use `hk-*` API key instead |
|
||||
| API key rejected | Key prefix not recognized | Recognized prefixes: `hk-`, `sk-`, `fw_`, `hz_`, `pk-` |
|
||||
| 403 Forbidden (browser) | Publishable key (`pk-*`) used from an unlisted origin | Add the origin to `AllowedOrigins` in `widget_security.go`; server-side, use a secret key instead |
|
||||
| API key rejected | Key spelled a way the door does not recognize | Two spellings exist: `sk-` and `pk-`. Anything else is refused, never defaulted. |
|
||||
| Route not found (404) | Endpoint not in gateway.json or host not in hostRoutes | Add endpoint to config or host to `router_engine.go` |
|
||||
| Config validation fails | JSON syntax error or schema violation | Run `make validate`; check against the v3 config schema |
|
||||
| WebSocket not connecting | Host not in `hostRoutes` | Add host entry; `httputil.ReverseProxy` handles WS natively |
|
||||
|
||||
@@ -164,23 +164,35 @@ secret = client.get_secret(
|
||||
### K8s Secret Sync (KMSSecret CRD)
|
||||
|
||||
```yaml
|
||||
apiVersion: kms.hanzo.ai/v1
|
||||
apiVersion: secrets.lux.network/v1alpha1
|
||||
kind: KMSSecret
|
||||
metadata:
|
||||
name: my-app-secrets
|
||||
name: my-app-kms-sync
|
||||
namespace: default
|
||||
spec:
|
||||
project: hanzo-paas
|
||||
environment: production
|
||||
syncInterval: 5m
|
||||
secretRef:
|
||||
name: my-app-secrets # K8s Secret to create/update
|
||||
secrets:
|
||||
- DATABASE_URL
|
||||
- REDIS_URL
|
||||
- HANZO_API_KEY
|
||||
hostAPI: https://kms.hanzo.ai
|
||||
resyncInterval: 60
|
||||
authentication:
|
||||
universalAuth:
|
||||
credentialsRef:
|
||||
secretName: my-app-iam-creds
|
||||
secretNamespace: default
|
||||
secretsScope:
|
||||
projectSlug: hanzo-paas
|
||||
envSlug: prod
|
||||
secretsPath: /my-app
|
||||
keys:
|
||||
- DATABASE_URL
|
||||
- HANZO_API_KEY
|
||||
managedSecretReference:
|
||||
creationPolicy: Orphan
|
||||
secretName: my-app-secrets # K8s Secret to create/update
|
||||
secretNamespace: default
|
||||
secretType: Opaque
|
||||
```
|
||||
|
||||
Every field is in the [KMSSecret reference](/docs/services/kms/kmssecret-crd).
|
||||
|
||||
The KMS operator (`hanzoai/kms-operator`) watches KMSSecret resources and auto-syncs to K8s Secrets.
|
||||
|
||||
### Helm Chart: Auto-Bootstrap
|
||||
|
||||
@@ -159,18 +159,34 @@ IAM_USERINFO_URL=https://hanzo.id/oauth/userinfo
|
||||
Secrets are KMS-first — no plaintext in manifests:
|
||||
|
||||
```yaml
|
||||
# KMSSecret CRD for auto-sync
|
||||
apiVersion: kms.hanzo.ai/v1
|
||||
# KMSSecret CRD for auto-sync — full field list at
|
||||
# /docs/services/kms/kmssecret-crd
|
||||
apiVersion: secrets.lux.network/v1alpha1
|
||||
kind: KMSSecret
|
||||
metadata:
|
||||
name: hanzo-paas
|
||||
name: paas-kms-sync
|
||||
namespace: hanzo
|
||||
spec:
|
||||
project: hanzo-paas
|
||||
environment: production
|
||||
secrets:
|
||||
- DOCKERHUB_USERNAME
|
||||
- DOCKERHUB_TOKEN
|
||||
- DIGITALOCEAN_ACCESS_TOKEN
|
||||
hostAPI: https://kms.hanzo.ai
|
||||
resyncInterval: 60
|
||||
authentication:
|
||||
universalAuth:
|
||||
credentialsRef:
|
||||
secretName: hanzo-paas-iam-creds
|
||||
secretNamespace: hanzo
|
||||
secretsScope:
|
||||
projectSlug: hanzo-paas
|
||||
envSlug: prod
|
||||
secretsPath: /paas
|
||||
keys:
|
||||
- DOCKERHUB_USERNAME
|
||||
- DOCKERHUB_TOKEN
|
||||
- DIGITALOCEAN_ACCESS_TOKEN
|
||||
managedSecretReference:
|
||||
creationPolicy: Orphan
|
||||
secretName: paas-secrets
|
||||
secretNamespace: hanzo
|
||||
secretType: Opaque
|
||||
```
|
||||
|
||||
## White-Label / Self-Host
|
||||
|
||||
@@ -66,7 +66,7 @@ terraform {
|
||||
}
|
||||
|
||||
provider "terraform-provider-hanzo" {
|
||||
api_key = "hk-..." # or set HANZO_API_KEY env var
|
||||
api_key = "sk-..." # or set HANZO_API_KEY env var
|
||||
}
|
||||
```
|
||||
|
||||
@@ -146,7 +146,7 @@ Platforms: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64,
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
HANZO_API_KEY=hk-... # API authentication (required)
|
||||
HANZO_API_KEY=sk-... # API authentication (required)
|
||||
HANZO_BASE_URL=https://... # Override API base URL
|
||||
```
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ use hanzo_tunnel::{connect, TunnelConfig, AppKind};
|
||||
async fn main() -> Result<(), hanzo_tunnel::TunnelError> {
|
||||
let conn = connect(TunnelConfig {
|
||||
relay_url: "wss://api.hanzo.ai/v1/relay".into(),
|
||||
auth_token: "hk-abc123".into(),
|
||||
auth_token: "sk-abc123".into(),
|
||||
app_kind: AppKind::Dev,
|
||||
display_name: "z-macbook".into(),
|
||||
capabilities: vec!["chat".into(), "exec".into()],
|
||||
|
||||
@@ -17,7 +17,7 @@ Databases are scoped to your organization. Creating one returns a ready-to-use c
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/sql \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "name": "app", "region": "us-east" }'
|
||||
@@ -59,7 +59,7 @@ Branches are copy-on-write clones that share storage with the parent until data
|
||||
- [Hanzo DB engine reference](/docs/services/sql) — pooling modes, PITR, and pgvector indexing
|
||||
- [Vector](/docs/vector) — a dedicated vector database for embedding-first workloads
|
||||
- [DocDB](/docs/docdb) — MongoDB-compatible document storage
|
||||
- [API Keys](/docs/api-keys) — authenticate with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
{/* domain-index */}
|
||||
|
||||
@@ -13,7 +13,7 @@ Buckets are scoped to your organization and provisioned through the control plan
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/s3 \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "name": "my-models", "versioning": true }'
|
||||
@@ -59,5 +59,5 @@ Hanzo Storage is the durable backing layer for model weights, training datasets,
|
||||
- [Hanzo S3 service reference](/docs/services/s3) — CLI, SDKs, policies, and lifecycle rules
|
||||
- [Registry](/docs/registry) — container and model artifacts stored on Hanzo S3
|
||||
- [Datastore](/docs/datastore) — analytics that read cold data directly from buckets
|
||||
- [API Keys](/docs/api-keys) — authenticate with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
@@ -9,11 +9,11 @@ A trace is one end-to-end request or agent run, broken into nested **observation
|
||||
|
||||
## Ingesting traces
|
||||
|
||||
Point any OpenTelemetry SDK or collector at the gateway. Tenancy travels in the export headers: the `hk-*` key authenticates and `X-Org-Id` scopes the data to your org.
|
||||
Point any OpenTelemetry SDK or collector at the gateway. Tenancy travels in the export headers: the `sk-*` key authenticates and `X-Org-Id` scopes the data to your org.
|
||||
|
||||
```bash
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.hanzo.ai/v1
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer hk-...,X-Org-Id=org_123"
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer sk-...,X-Org-Id=org_123"
|
||||
export OTEL_SERVICE_NAME=checkout-api
|
||||
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production"
|
||||
```
|
||||
@@ -27,11 +27,11 @@ Traces are queried over REST. List endpoints return `{ data, meta }` with `page`
|
||||
```bash
|
||||
# List recent traces
|
||||
curl "https://api.hanzo.ai/v1/o11y/traces?page=1&limit=50" \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
|
||||
# Fetch one trace with its observations + scores
|
||||
curl https://api.hanzo.ai/v1/o11y/traces/tr_abc123 \
|
||||
-H "Authorization: Bearer hk-..."
|
||||
-H "Authorization: Bearer sk-..."
|
||||
```
|
||||
|
||||
```json
|
||||
@@ -66,4 +66,4 @@ Each trace carries request-level metadata and a list of observations. An observa
|
||||
- [Sessions](/docs/sessions) — traces grouped into multi-turn runs
|
||||
- [Scores](/docs/scores) — evaluations attached to traces and observations
|
||||
- [Logs](/docs/logs) and [Metrics](/docs/metrics) — the other two observability signals
|
||||
- [API Keys](/docs/api-keys) — creating the `hk-*` key these calls use
|
||||
- [API Keys](/docs/api-keys) — creating the `sk-*` key these calls use
|
||||
|
||||
@@ -13,7 +13,7 @@ A collection is a logical resource in the shared Hanzo Vector backend, scoped to
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.hanzo.ai/v1/vector \
|
||||
-H "Authorization: Bearer hk-..." \
|
||||
-H "Authorization: Bearer sk-..." \
|
||||
-H "X-Org-Id: org_a1b2c3" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
@@ -46,5 +46,5 @@ The backend is API-compatible with Qdrant, so any Qdrant client works against yo
|
||||
- [Hanzo Vector service reference](/docs/services/vector) — full collection, points, and snapshot API
|
||||
- [Embeddings](/docs/embeddings) — generate the vectors you store here
|
||||
- [Search](/docs/search) — full-text and hybrid indexes
|
||||
- [API Keys](/docs/api-keys) — authenticate with `hk-` keys
|
||||
- [API Keys](/docs/api-keys) — authenticate with `sk-` keys
|
||||
- [API Reference](/docs/openapi) — every endpoint at `api.hanzo.ai`
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { DOCUMENT, loadDocument, type KeyType } from './openapi-doc';
|
||||
|
||||
// No page may spell a Hanzo API key a way cloud does not mint.
|
||||
//
|
||||
// This is check-endpoints' rule applied to the other half of a first call. That
|
||||
// guard stops a page naming a route the API does not serve; this one stops a
|
||||
// page naming a CREDENTIAL the API does not issue, which fails a reader one step
|
||||
// earlier — they cannot even ask for the key the page tells them to get.
|
||||
//
|
||||
// It was written because docs.hanzo.ai documented three key types and cloud has
|
||||
// two. `hk-` and `hz-` appeared on 148 pages, including the authentication page,
|
||||
// and neither has ever existed: cloud's door takes `pk-` and `sk-` and refuses
|
||||
// everything else. Prose cannot be trusted to remember that, so it is checked.
|
||||
//
|
||||
// BOTH directions, which is what makes the pages underivable-from-memory:
|
||||
//
|
||||
// unknown a page spells a prefix the document does not carry -> fail
|
||||
// missing the authentication page omits a class the document DOES carry -> fail
|
||||
//
|
||||
// So renaming a class in cloud fails the build, adding one fails the build until
|
||||
// it is documented, and removing one fails the build while a page still teaches
|
||||
// it. A prefix on these pages is therefore a projection of cloud with a checked
|
||||
// arrow back to it, not a value someone typed and nobody re-read.
|
||||
|
||||
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const APP_ROOT = path.resolve(SCRIPT_DIR, '..');
|
||||
const CONTENT = path.join(APP_ROOT, 'content/docs');
|
||||
|
||||
/** Where the key classes are taught. The `missing` half of the rule applies here. */
|
||||
const AUTHORITY = 'api-keys.mdx';
|
||||
|
||||
// Ported upstream docs describe other people's credentials — Stripe's `sk_`,
|
||||
// GitHub's `ghp_` — mirrored, not authored here. Same exemption check-endpoints
|
||||
// makes, for the same reason: they are not ours to hold to our document.
|
||||
const SKIP = ['projects/'];
|
||||
|
||||
/**
|
||||
* What a credential looks like, and where one appears. Both halves are needed,
|
||||
* and the first half is read off the document rather than chosen.
|
||||
*
|
||||
* SHAPE. A prefix is some lowercase letters and a hyphen, and how many letters
|
||||
* is not this file's opinion — it is measured from the prefixes the document
|
||||
* carries. `sk-` and `pk-` are two letters, so two letters is what a Hanzo key
|
||||
* prefix is today, and the day cloud mints a longer one the guard widens with
|
||||
* it. That one derived number is what separates `hk-your-api-key` from a bare
|
||||
* `your-api-key`: the first wears a prefix, the second IS the placeholder.
|
||||
*
|
||||
* POSITION. Shape alone separates nothing — `us-east-1` and `do-sfo3` are two
|
||||
* letters and a hyphen too. A credential appears in exactly three places, and
|
||||
* none of them holds a region or a model:
|
||||
*
|
||||
* PRESENTED Bearer hk-… the header a reader will send
|
||||
* ASSIGNED api_key="hk-…", HANZO_API_KEY=… an identifier ending in key or
|
||||
* token, given a prefixed value
|
||||
* NAMED `hk-` `hk-*` `hk-...` the prefix as its own code span
|
||||
*
|
||||
* This is check-endpoints' anchor argument applied to credentials: that guard
|
||||
* only reads a path written against `api.hanzo.ai`, because a bare `/v1/acs` is
|
||||
* on somebody else's host. A bare hyphenated word is somebody else's noun.
|
||||
*
|
||||
* NAMED deliberately does NOT admit a body that says the word key. It looks like
|
||||
* it should — `hk-your-api-key` is plainly a credential — but every literal of
|
||||
* that form on this site is already in a Bearer or an assignment, while the
|
||||
* shape by itself sweeps up `iam-secrets`, `oidc-client-secret`, `data-api-key`
|
||||
* and every other Kubernetes Secret named on these pages. It bought nothing and
|
||||
* cost fourteen wrong findings.
|
||||
*
|
||||
* One thing this cannot tell apart, and does not pretend to: an upstream
|
||||
* provider's key that happens to share a spelling. `OPENAI_API_KEY=sk-…` reads
|
||||
* as `sk-`, which is also Hanzo's secret prefix, so it passes. It would only
|
||||
* ever pass — a shared spelling cannot produce a wrong refusal — so the
|
||||
* ambiguity costs a reader nothing.
|
||||
*/
|
||||
const PRESENTED = (n: string) => new RegExp(String.raw`\bBearer\s+["'\`]?([a-z]{${n}}-)`, 'gi');
|
||||
const ASSIGNED = (n: string) =>
|
||||
new RegExp(
|
||||
String.raw`[A-Za-z_][A-Za-z0-9_-]*(?:key|token)["'\`]?\s*(?:[:=]\s*["'\`]?|\(\s*["'\`])([a-z]{${n}}-)`,
|
||||
'gi',
|
||||
);
|
||||
const NAMED = (n: string) =>
|
||||
new RegExp(String.raw`(?:\`|")([a-z]{${n}}-)(\*|\.\.\.)?(?:\`|")`, 'g');
|
||||
|
||||
/** The prefix lengths the document uses, as a regex quantifier: `2`, or `2,4`. */
|
||||
export function prefixShape(keys: KeyType[]): string {
|
||||
const lens = [...new Set(keys.map((k) => k.prefix.replace('-', '').length))].sort((a, b) => a - b);
|
||||
return lens.length === 1 ? String(lens[0]) : `${lens[0]},${lens.at(-1)}`;
|
||||
}
|
||||
|
||||
function walk(dir: string, out: string[] = []): string[] {
|
||||
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const p = path.join(dir, e.name);
|
||||
if (e.isDirectory()) walk(p, out);
|
||||
else if (e.isFile() && (e.name.endsWith('.mdx') || e.name.endsWith('.md'))) out.push(p);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* A page as the build compiles it, with `<include>` spliced in.
|
||||
*
|
||||
* The key-type table is generated from the document and included, so reading the
|
||||
* raw source would find no prefixes on the very page whose job is to teach them
|
||||
* — and the `missing` half of the rule would fail on the page that is most
|
||||
* right. remarkInclude resolves relative to the including file; so does this.
|
||||
*
|
||||
* A target that is not there THROWS, and that is not defensiveness. The first
|
||||
* version returned the unresolved tag instead, and an include written one
|
||||
* directory short passed every check while the page it was on rendered nothing
|
||||
* at all — the body silently empty, frontmatter and chrome intact, so the page
|
||||
* still had its title in the sidebar and its description in the search index.
|
||||
* A guard that tolerates a broken include is checking a page nobody can read.
|
||||
*/
|
||||
const INCLUDE = /<include>\s*([^<\s#]+)[^<]*<\/include>/g;
|
||||
|
||||
function compiled(file: string, depth = 0): string {
|
||||
const src = fs.readFileSync(file, 'utf8');
|
||||
if (depth > 3) return src;
|
||||
return src.replace(INCLUDE, (_whole, rel: string) => {
|
||||
const target = path.resolve(path.dirname(file), rel);
|
||||
if (!fs.existsSync(target)) {
|
||||
throw new Error(`${file} includes ${rel}, which does not exist — the page renders empty`);
|
||||
}
|
||||
return compiled(target, depth + 1);
|
||||
});
|
||||
}
|
||||
|
||||
/** Every credential prefix a page spells, deduplicated and lowercased. */
|
||||
export function prefixesIn(src: string, shape: string): Set<string> {
|
||||
const found = new Set<string>();
|
||||
for (const re of [PRESENTED(shape), ASSIGNED(shape), NAMED(shape)]) {
|
||||
for (const m of src.matchAll(re)) found.add(m[1].toLowerCase());
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
export interface KeyCheck {
|
||||
/** How many credential literals were read across the content tree. */
|
||||
checked: number;
|
||||
/** The classes the document carries — what the pages are checked against. */
|
||||
keys: KeyType[];
|
||||
/** Page, and the prefix it spells that cloud does not mint. */
|
||||
unknown: Array<[string, string]>;
|
||||
/** Classes the document carries that the authority page never teaches. */
|
||||
missing: string[];
|
||||
}
|
||||
|
||||
export function checkKeys(): KeyCheck {
|
||||
const doc = loadDocument(DOCUMENT);
|
||||
const known = new Set(doc.keys.map((k) => k.prefix));
|
||||
const shape = prefixShape(doc.keys);
|
||||
|
||||
const unknown: Array<[string, string]> = [];
|
||||
let checked = 0;
|
||||
let taught = new Set<string>();
|
||||
|
||||
for (const file of walk(CONTENT)) {
|
||||
const rel = path.relative(CONTENT, file);
|
||||
if (SKIP.some((s) => rel.startsWith(s))) continue;
|
||||
const found = prefixesIn(compiled(file), shape);
|
||||
checked += found.size;
|
||||
if (rel === AUTHORITY) taught = found;
|
||||
for (const p of found) if (!known.has(p)) unknown.push([rel, p]);
|
||||
}
|
||||
|
||||
return {
|
||||
checked,
|
||||
keys: doc.keys,
|
||||
unknown,
|
||||
missing: doc.keys.filter((k) => !taught.has(k.prefix)).map((k) => `${k.name} (${k.prefix})`),
|
||||
};
|
||||
}
|
||||
|
||||
/** Report every page that spells a prefix cloud does not mint. */
|
||||
export function report(unknown: Array<[string, string]>): void {
|
||||
const byFile = new Map<string, string[]>();
|
||||
for (const [f, p] of unknown) byFile.set(f, [...(byFile.get(f) ?? []), p]);
|
||||
for (const [f, ps] of byFile) console.error(` ${f}: ${[...new Set(ps)].join(', ')}`);
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
const { checked, keys, unknown, missing } = checkKeys();
|
||||
console.log(
|
||||
`[keys] the document mints ${keys.map((k) => `${k.name} ${k.prefix}`).join(', ')}; ` +
|
||||
`${checked} credential literals read`,
|
||||
);
|
||||
if (unknown.length) {
|
||||
console.error(`[keys] FAIL — ${unknown.length} spell a prefix cloud does not mint:`);
|
||||
report(unknown);
|
||||
}
|
||||
if (missing.length) {
|
||||
console.error(`[keys] FAIL — ${AUTHORITY} never teaches: ${missing.join(', ')}`);
|
||||
}
|
||||
if (unknown.length || missing.length) process.exit(1);
|
||||
console.log('[keys] every prefix on every page is one the document carries');
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { parse as parseYaml } from 'yaml';
|
||||
import { DOCUMENT, loadDocument, type Document, type Operation } from './openapi-doc';
|
||||
import { DOCUMENT, loadDocument, secretKey, type Document, type Operation } from './openapi-doc';
|
||||
import { SDKS, cli, http, mcp } from './openapi-surfaces';
|
||||
import { MCP_DOOR, loadMcpTools } from './sync-mcp-tools';
|
||||
import type { CliCommand } from './sync-cli-commands';
|
||||
@@ -253,7 +253,7 @@ function renderIndex(flows: Flow[], doc: Document): string {
|
||||
L.push('## One credential');
|
||||
L.push('');
|
||||
L.push('```bash');
|
||||
L.push('export HANZO_API_KEY=hk-... # console.hanzo.ai');
|
||||
L.push(`export HANZO_API_KEY=${secretKey(doc).prefix}... # console.hanzo.ai`);
|
||||
L.push('hanzo auth login # or sign in through Hanzo IAM');
|
||||
L.push('```');
|
||||
L.push('');
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { DOCUMENT, loadDocument, type KeyType } from './openapi-doc';
|
||||
import { text } from './mdx';
|
||||
|
||||
// THE KEY TYPES, generated from THE document.
|
||||
//
|
||||
// /docs/api-keys is the first page a reader needs and the one that was most
|
||||
// wrong: it documented three key types, cloud mints two, and neither of the
|
||||
// other two has ever existed. It went wrong the ordinary way — someone typed
|
||||
// what a key looked like, and nothing afterwards ever compared the two.
|
||||
//
|
||||
// So the table is not typed. `/v1/keys` is the key resource, its Go source names
|
||||
// each class beside its prefix, zipdoc lifts those comments into the document,
|
||||
// and openapi-doc reads them back as Document.keys. This renders that. The prose
|
||||
// around it on the page stays authored, because how to keep a credential out of
|
||||
// a git history is not something the API describes — but every prefix, every
|
||||
// class name and every "what it resolves to" comes from the handler.
|
||||
//
|
||||
// It is included rather than routed: `<include>` splices this into api-keys.mdx
|
||||
// at compile time, so the fact appears in the page and this file is not a page.
|
||||
// It lives outside content/ because source.config globs `**/*.mdx` there and
|
||||
// would give a fragment its own URL.
|
||||
|
||||
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const APP_ROOT = path.resolve(SCRIPT_DIR, '..');
|
||||
export const KEY_TYPES_FRAGMENT = path.join(APP_ROOT, 'generated/key-types.mdx');
|
||||
|
||||
/**
|
||||
* The one-line reading of a class, for the table's last column.
|
||||
*
|
||||
* The document states each class as `"secret" (sk-, session-equivalent, belongs
|
||||
* on a server)`, and that parenthetical is the sentence worth publishing — it is
|
||||
* the handler's own summary of what the class is FOR. Where a class carries no
|
||||
* note, the class name stands alone rather than inventing one.
|
||||
*/
|
||||
const reading = (k: KeyType): string => (k.note ? text(k.note) : `The ${k.name} key.`);
|
||||
|
||||
export function genKeyTypes(): KeyType[] {
|
||||
const doc = loadDocument(DOCUMENT);
|
||||
|
||||
const rows = doc.keys
|
||||
.map((k) => `| \`${k.prefix}\` | ${text(k.name)} | ${reading(k)} |`)
|
||||
.join('\n');
|
||||
|
||||
const browser = doc.keys.find((k) => k.publishable)!;
|
||||
const server = doc.keys.filter((k) => !k.publishable);
|
||||
|
||||
const body = `{/* Generated by scripts/gen-key-types.ts from hanzoai/cloud's openapi.yaml.
|
||||
Every value below is the /v1/keys handler's own doc comment. Do not edit. */}
|
||||
|
||||
| Prefix | Type | What it is |
|
||||
|--------|------|------------|
|
||||
${rows}
|
||||
|
||||
${
|
||||
server.length === 1
|
||||
? `\`${server[0].prefix}\` belongs on a server and \`${browser.prefix}\` may ship in a browser bundle.`
|
||||
: `\`${browser.prefix}\` may ship in a browser bundle; every other type belongs on a server.`
|
||||
} A key spelled any other way is refused at the door — never guessed at, never
|
||||
defaulted to the safer-sounding one.
|
||||
`;
|
||||
|
||||
fs.mkdirSync(path.dirname(KEY_TYPES_FRAGMENT), { recursive: true });
|
||||
fs.writeFileSync(KEY_TYPES_FRAGMENT, body);
|
||||
console.log(
|
||||
`[keys] ${doc.keys.map((k) => `${k.name} ${k.prefix}`).join(', ')} -> ` +
|
||||
path.relative(APP_ROOT, KEY_TYPES_FRAGMENT),
|
||||
);
|
||||
return doc.keys;
|
||||
}
|
||||
|
||||
if (import.meta.main) genKeyTypes();
|
||||
@@ -1,7 +1,15 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { DOCUMENT, isInternal, loadDocument, release, type Document, type Operation } from './openapi-doc';
|
||||
import {
|
||||
DOCUMENT,
|
||||
isInternal,
|
||||
loadDocument,
|
||||
release,
|
||||
secretKey,
|
||||
type Document,
|
||||
type Operation,
|
||||
} from './openapi-doc';
|
||||
import { toolOperations } from './openapi-surfaces';
|
||||
import { load, MCP_DOOR, type McpCatalog, type McpTool } from './sync-mcp-tools';
|
||||
import { code, fence, firstSentence, prose, text, yamlString } from './mdx';
|
||||
@@ -788,7 +796,7 @@ function renderIndex(cat: McpCatalog, doc: Document, groups: Map<string, McpTool
|
||||
L.push(
|
||||
...fence(
|
||||
'bash',
|
||||
`export HANZO_API_KEY=hk-...\ncurl -X POST ${cat.door} \\\n -H "Authorization: Bearer $HANZO_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'`,
|
||||
`export HANZO_API_KEY=${secretKey(doc).prefix}...\ncurl -X POST ${cat.door} \\\n -H "Authorization: Bearer $HANZO_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'`,
|
||||
),
|
||||
);
|
||||
L.push('');
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
DOCUMENT,
|
||||
loadDocument,
|
||||
publicDocument,
|
||||
secretKey,
|
||||
type Document,
|
||||
type Operation,
|
||||
type Product,
|
||||
@@ -263,7 +264,9 @@ function renderIndex(products: Product[], doc: Document): string {
|
||||
L.push('');
|
||||
L.push('## Authentication');
|
||||
L.push('');
|
||||
L.push('Every product accepts the same bearer credential — a Hanzo IAM JWT or an `hk-` API key.');
|
||||
L.push(
|
||||
`Every product accepts the same bearer credential — a Hanzo IAM JWT or a \`${secretKey(doc).prefix}\` secret key.`,
|
||||
);
|
||||
L.push('');
|
||||
L.push('```bash');
|
||||
L.push(`curl -H "Authorization: Bearer $HANZO_API_KEY" ${doc.server}/v1/models`);
|
||||
|
||||
@@ -92,12 +92,34 @@ export interface Product {
|
||||
operations: Operation[];
|
||||
}
|
||||
|
||||
/**
|
||||
* One class of API key, as the document states it at `/v1/keys`.
|
||||
*
|
||||
* The class name is the product's own noun — it is the literal a caller sends
|
||||
* as `{"type": …}` — and the prefix is what a key of that class is spelled
|
||||
* with. `publishable` is not a judgement made here; it is the class the product
|
||||
* NAMES publishable, which is the same word cloud's `PublishablePrefix` is
|
||||
* named after.
|
||||
*/
|
||||
export interface KeyType {
|
||||
/** The class as a caller names it: `secret`, `publishable`. */
|
||||
name: string;
|
||||
/** The spelling a key of this class carries: `sk-`, `pk-`. */
|
||||
prefix: string;
|
||||
/** What the document says the class is, verbatim from inside the parens. */
|
||||
note: string;
|
||||
/** True for the class the product calls publishable — the browser-safe one. */
|
||||
publishable: boolean;
|
||||
}
|
||||
|
||||
export interface Document {
|
||||
title: string;
|
||||
version: string;
|
||||
description: string;
|
||||
server: string;
|
||||
securitySchemes: Record<string, any>;
|
||||
/** Every class of API key the document says `/v1/keys` mints — see keyTypes. */
|
||||
keys: KeyType[];
|
||||
/** Products docs.hanzo.ai publishes. Excludes `internal` — see isInternal. */
|
||||
products: Product[];
|
||||
/** The operator surface, grouped the same way. Never published publicly. */
|
||||
@@ -143,6 +165,17 @@ const INTERNAL_IDS = new Set(['get_v1_commerce_admin_catalog']);
|
||||
export const isInternal = (op: Operation): boolean =>
|
||||
op.product === INTERNAL_PRODUCT || INTERNAL_IDS.has(op.id);
|
||||
|
||||
/**
|
||||
* The key a server presents: the one class that is NOT publishable.
|
||||
*
|
||||
* Every generated page that shows a curl or an env var shows this one, and each
|
||||
* used to spell it as a literal — three copies of a value the document states,
|
||||
* which is how three generated pages came to teach a prefix cloud has never
|
||||
* minted. Asking the document is one expression and cannot drift.
|
||||
*/
|
||||
export const secretKey = (doc: Document): KeyType =>
|
||||
doc.keys.find((k) => !k.publishable) ?? doc.keys[0];
|
||||
|
||||
/**
|
||||
* The document with the operator surface taken out, for publishing.
|
||||
*
|
||||
@@ -183,6 +216,84 @@ export function deref(raw: any, node: any, depth = 0): any {
|
||||
return deref(raw, cur, depth + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The classes of API key, read out of the document rather than restated.
|
||||
*
|
||||
* `/v1/keys` IS the key resource: one endpoint, and the class is a field on it.
|
||||
* Its Go source spells each class beside its prefix — `"secret" (sk-, …)`,
|
||||
* `"publishable" (pk-, …)` — zipdoc lifts those doc comments into this
|
||||
* document, and this reads them back. So a page saying `sk-` is saying what the
|
||||
* handler says, one hop away, instead of what someone remembered.
|
||||
*
|
||||
* The pairs are read from EVERY description the key resource carries and the
|
||||
* readings must AGREE. Two doc comments state them today — the `type` field a
|
||||
* caller sends and the `type` field they read back — written in different words
|
||||
* at different places in cloud. Requiring agreement means a rename that lands in
|
||||
* one of them and not the other stops the build here rather than publishing two
|
||||
* answers.
|
||||
*
|
||||
* Refusing is the whole point of the exercise: an empty or contradictory reading
|
||||
* throws, because a docs build that quietly published no key types would be the
|
||||
* same failure — a reader who cannot learn which credential to ask for — wearing
|
||||
* a green checkmark.
|
||||
*/
|
||||
const KEY_CLASS = /"?\b([a-z][a-z-]{2,20})"?\s*\((([a-z]{2,6})-)([,)][^)]*)?\)/g;
|
||||
|
||||
export function keyTypes(raw: any): KeyType[] {
|
||||
const item = raw?.paths?.['/v1/keys'];
|
||||
if (!item) {
|
||||
// Every generated page prints a bearer credential, so none of them can be
|
||||
// written without knowing how one is spelled. Stopping here is the honest
|
||||
// failure; the alternative is a whole site of curls with a blank key.
|
||||
throw new Error('the document does not serve /v1/keys — no page can state how a key is spelled');
|
||||
}
|
||||
|
||||
// Every sentence the key resource carries: its operations, their parameters,
|
||||
// and the schemas they send and return. Naming one schema would tie this to a
|
||||
// Go type name; walking what the path item reaches ties it to the resource.
|
||||
const said: string[] = [];
|
||||
const collect = (node: any, depth = 0) => {
|
||||
if (!node || typeof node !== 'object' || depth > 6) return;
|
||||
const d = deref(raw, node, 0);
|
||||
if (typeof d?.description === 'string') said.push(d.description);
|
||||
for (const v of Object.values(d)) if (v && typeof v === 'object') collect(v, depth + 1);
|
||||
};
|
||||
collect(item);
|
||||
|
||||
// class -> prefix, and every reading of a class must give the same prefix.
|
||||
const seen = new Map<string, KeyType>();
|
||||
for (const text of said) {
|
||||
for (const m of text.replace(/\s+/g, ' ').matchAll(KEY_CLASS)) {
|
||||
const name = m[1];
|
||||
const prefix = `${m[3]}-`;
|
||||
const note = (m[4] ?? '').replace(/^[,)]\s*/, '').trim();
|
||||
const prior = seen.get(name);
|
||||
if (prior && prior.prefix !== prefix) {
|
||||
throw new Error(
|
||||
`the document spells the ${name} key both ${prior.prefix} and ${prefix} — cloud says it two ways`,
|
||||
);
|
||||
}
|
||||
// Keep the longest note: the same pair is stated tersely in one place and
|
||||
// with its reason in another, and the reason is the half worth publishing.
|
||||
if (!prior || note.length > prior.note.length) {
|
||||
seen.set(name, { name, prefix, note, publishable: name === 'publishable' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const found = [...seen.values()];
|
||||
if (found.length < 2) {
|
||||
throw new Error(
|
||||
`/v1/keys names ${found.length} key class(es) — the document stopped stating them, so no page can`,
|
||||
);
|
||||
}
|
||||
if (!found.some((k) => k.publishable)) {
|
||||
throw new Error('no key class is named publishable — nothing may be shipped in a browser');
|
||||
}
|
||||
// Publishable last: a reader meets the default before the exception.
|
||||
return found.sort((a, b) => Number(a.publishable) - Number(b.publishable));
|
||||
}
|
||||
|
||||
/** `agents` -> `Agents`; `Roles & Permissions` and `MFA` pass through. */
|
||||
const titleCase = (name: string): string =>
|
||||
/^[a-z]/.test(name) ? name[0].toUpperCase() + name.slice(1) : name;
|
||||
@@ -365,6 +476,7 @@ export function loadDocument(file: string): Document {
|
||||
description: firstSentence(info.description ?? ''),
|
||||
server: raw.servers?.[0]?.url ?? 'https://api.hanzo.ai',
|
||||
securitySchemes: raw.components?.securitySchemes ?? {},
|
||||
keys: keyTypes(raw),
|
||||
// Only products the document actually serves operations for. A tag with no
|
||||
// operations is not a product page — it is a tag we have not filled in yet.
|
||||
//
|
||||
|
||||
@@ -3,11 +3,13 @@ import { genOpenapiPages } from './gen-openapi-pages';
|
||||
import { genFlowPages } from './gen-flow-pages';
|
||||
import { genMcpPages } from './gen-mcp-pages';
|
||||
import { genPricingPage } from './gen-pricing-page';
|
||||
import { genKeyTypes } from './gen-key-types';
|
||||
import { syncCliCommands } from './sync-cli-commands';
|
||||
import { syncMcpTools } from './sync-mcp-tools';
|
||||
import { syncProjectDocs } from './sync-project-docs';
|
||||
import { sanitizeMdx } from './sanitize-mdx';
|
||||
import { checkEndpoints, report } from './check-endpoints';
|
||||
import { checkKeys, report as reportKeys } from './check-keys';
|
||||
|
||||
async function main() {
|
||||
// The document first, then its projections: the reference (one page per
|
||||
@@ -20,6 +22,9 @@ async function main() {
|
||||
await genOpenapiPages();
|
||||
await genFlowPages();
|
||||
await genMcpPages();
|
||||
// The key types the authentication page teaches. Also from the document, and
|
||||
// BEFORE the checks below, because the check reads what it writes.
|
||||
genKeyTypes();
|
||||
// The pricing page is the billing API's own answer, not the document's — it
|
||||
// reads /v1/pricing, so it neither needs nor blocks the three above.
|
||||
await genPricingPage();
|
||||
@@ -43,6 +48,23 @@ async function main() {
|
||||
throw new Error(`${unknown.length} mentions name endpoints the document does not have`);
|
||||
}
|
||||
console.log(`[endpoints] ${checked} mentions checked, every one in the document`);
|
||||
|
||||
// The same rule for the credential, which a reader needs one step before the
|
||||
// endpoint: no page may spell a key a way cloud does not mint, and the page
|
||||
// that teaches the key types must teach all of them.
|
||||
const keys = checkKeys();
|
||||
if (keys.unknown.length) reportKeys(keys.unknown);
|
||||
if (keys.missing.length) console.error(` api-keys.mdx never teaches: ${keys.missing.join(', ')}`);
|
||||
if (keys.unknown.length || keys.missing.length) {
|
||||
throw new Error(
|
||||
`${keys.unknown.length} mentions spell a key prefix cloud does not mint` +
|
||||
(keys.missing.length ? `, and ${keys.missing.length} key type(s) go untaught` : ''),
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
`[keys] ${keys.checked} credential literals checked against ` +
|
||||
keys.keys.map((k) => k.prefix).join(' / '),
|
||||
);
|
||||
}
|
||||
|
||||
await main().catch((e) => {
|
||||
|
||||
@@ -291,7 +291,7 @@ export default function HomePage() {
|
||||
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-...")
|
||||
client = Hanzo(api_key="sk-...")
|
||||
r = client.chat.completions.create(
|
||||
model="zen5",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
@@ -304,7 +304,7 @@ print(r.choices[0].message.content)`}</code></pre>
|
||||
<Terminal className="h-3.5 w-3.5" /> curl
|
||||
</div>
|
||||
<pre className="p-5 text-sm overflow-x-auto"><code>{`curl https://api.hanzo.ai/v1/chat/completions \\
|
||||
-H "Authorization: Bearer hk-..." \\
|
||||
-H "Authorization: Bearer sk-..." \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"model": "zen5",
|
||||
|
||||
@@ -97,7 +97,7 @@ pip install hanzoai
|
||||
```python
|
||||
from hanzoai import TrainingClient
|
||||
|
||||
client = TrainingClient(api_key="hk-...")
|
||||
client = TrainingClient(api_key="sk-...")
|
||||
|
||||
# Single trace
|
||||
client.submit_trace(
|
||||
|
||||
@@ -85,7 +85,7 @@ Set `stream: true` to receive server-sent events:
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-key")
|
||||
client = Hanzo(api_key="sk-your-key")
|
||||
|
||||
stream = client.chat.completions.create(
|
||||
model="zen4",
|
||||
|
||||
@@ -38,7 +38,7 @@ curl https://api.hanzo.ai/v1/embeddings \
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-key")
|
||||
client = Hanzo(api_key="sk-your-key")
|
||||
|
||||
response = client.embeddings.create(
|
||||
model="zen-embedding",
|
||||
@@ -54,7 +54,7 @@ for embedding in response.data:
|
||||
```ts
|
||||
import Hanzo from '@hanzo/ai'
|
||||
|
||||
const client = new Hanzo({ apiKey: 'hk-your-key' })
|
||||
const client = new Hanzo({ apiKey: 'sk-your-key' })
|
||||
|
||||
const response = await client.embeddings.create({
|
||||
model: 'zen-embedding',
|
||||
|
||||
@@ -15,7 +15,7 @@ https://api.hanzo.ai/v1
|
||||
|
||||
## Authentication
|
||||
|
||||
All API requests require a `HANZO_API_KEY` (prefix: `hk-`) obtained from [console.hanzo.ai](https://console.hanzo.ai).
|
||||
All API requests require a `HANZO_API_KEY` (prefix: `sk-`) obtained from [console.hanzo.ai](https://console.hanzo.ai).
|
||||
|
||||
```bash
|
||||
Authorization: Bearer $HANZO_API_KEY
|
||||
@@ -44,7 +44,7 @@ curl https://api.hanzo.ai/v1/chat/completions \
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen4",
|
||||
@@ -58,7 +58,7 @@ print(response.choices[0].message.content)
|
||||
```typescript
|
||||
import Hanzo from "hanzoai";
|
||||
|
||||
const client = new Hanzo({ apiKey: "hk-your-api-key" });
|
||||
const client = new Hanzo({ apiKey: "sk-your-api-key" });
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: "zen4",
|
||||
|
||||
@@ -54,7 +54,7 @@ curl https://api.hanzo.ai/v1/messages \
|
||||
from anthropic import Anthropic
|
||||
|
||||
client = Anthropic(
|
||||
api_key="hk-your-api-key",
|
||||
api_key="sk-your-api-key",
|
||||
base_url="https://api.hanzo.ai/v1",
|
||||
)
|
||||
|
||||
@@ -72,7 +72,7 @@ print(message.content[0].text)
|
||||
import Anthropic from "@anthropic-ai/sdk";
|
||||
|
||||
const client = new Anthropic({
|
||||
apiKey: "hk-your-api-key",
|
||||
apiKey: "sk-your-api-key",
|
||||
baseURL: "https://api.hanzo.ai/v1",
|
||||
});
|
||||
|
||||
|
||||
@@ -53,5 +53,5 @@ it.
|
||||
|
||||
1. Create an account at [console.hanzo.ai](https://console.hanzo.ai)
|
||||
2. Your **$5 free credit** is applied automatically
|
||||
3. Generate an API key (prefix: `hk-`)
|
||||
3. Generate an API key (prefix: `sk-`)
|
||||
4. Start making requests to `https://api.hanzo.ai/v1`
|
||||
|
||||
@@ -18,7 +18,7 @@ pip install hanzoai
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen4",
|
||||
|
||||
@@ -31,7 +31,7 @@ Zen models are served at `https://api.hanzo.ai/v1`. Get a key from the
|
||||
|
||||
```bash
|
||||
curl https://api.hanzo.ai/v1/chat/completions \
|
||||
-H "Authorization: Bearer hk-your-api-key" \
|
||||
-H "Authorization: Bearer sk-your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model": "zen5", "messages": [{"role": "user", "content": "Hello, Zen."}]}'
|
||||
```
|
||||
@@ -41,7 +41,7 @@ curl https://api.hanzo.ai/v1/chat/completions \
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen5",
|
||||
@@ -55,7 +55,7 @@ print(response.choices[0].message.content)
|
||||
```ts
|
||||
import Hanzo from '@hanzo/ai'
|
||||
|
||||
const client = new Hanzo({ apiKey: 'hk-your-api-key' })
|
||||
const client = new Hanzo({ apiKey: 'sk-your-api-key' })
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'zen5',
|
||||
@@ -83,7 +83,7 @@ from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
base_url="https://api.hanzo.ai/v1",
|
||||
api_key="hk-your-api-key",
|
||||
api_key="sk-your-api-key",
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
@@ -94,7 +94,7 @@ response = client.chat.completions.create(
|
||||
|
||||
Model ids do not carry across vendors — `zen5` is a Hanzo model and resolves
|
||||
only here.
|
||||
- **API key prefix**: `hk-`
|
||||
- **API key prefix**: `sk-`
|
||||
- **Endpoint**: `api.hanzo.ai/v1`
|
||||
|
||||
## All Models
|
||||
|
||||
@@ -7,7 +7,7 @@ description: Complete Zen LM model family — 80+ models across text, code, visi
|
||||
|
||||
Zen LM by Hanzo AI is a comprehensive model family built using **Zen MoDE (Mixture of Diverse Experts)** — diverse expert specializations fused into a unified, high-performance family.
|
||||
|
||||
All API models are accessed at `api.hanzo.ai`. API key prefix: `hk-`. Open-weight models are available on [HuggingFace](https://huggingface.co/zenlm).
|
||||
All API models are accessed at `api.hanzo.ai`. API key prefix: `sk-`. Open-weight models are available on [HuggingFace](https://huggingface.co/zenlm).
|
||||
|
||||
---
|
||||
|
||||
@@ -124,7 +124,7 @@ Agent-optimized models for tool use, planning, and autonomous workflows.
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen4",
|
||||
@@ -136,7 +136,7 @@ print(response.choices[0].message.content)
|
||||
```typescript
|
||||
import Hanzo from '@hanzo/ai';
|
||||
|
||||
const client = new Hanzo({ apiKey: 'hk-your-api-key' });
|
||||
const client = new Hanzo({ apiKey: 'sk-your-api-key' });
|
||||
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'zen4',
|
||||
|
||||
@@ -35,7 +35,7 @@ A 3D asset generation model that creates meshes, textures, and materials from te
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
# Coming soon
|
||||
response = client.models.generate_3d(
|
||||
|
||||
@@ -39,7 +39,7 @@ A 32B dense transformer designed for agentic workflows with native tool use, mul
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
tools = [
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ An image editing model for inpainting, outpainting, and instruction-based modifi
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.images.edit(
|
||||
model="zen-artist-edit",
|
||||
|
||||
@@ -36,7 +36,7 @@ A dedicated image generation model capable of producing high-resolution images a
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.images.generate(
|
||||
model="zen-artist",
|
||||
|
||||
@@ -52,7 +52,7 @@ print(tokenizer.decode(outputs[0], skip_special_tokens=True))
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen-code",
|
||||
|
||||
@@ -53,7 +53,7 @@ print(tokenizer.decode(outputs[0], skip_special_tokens=True))
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen5-coder-flash",
|
||||
|
||||
@@ -53,7 +53,7 @@ print(tokenizer.decode(outputs[0], skip_special_tokens=True))
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen5-coder",
|
||||
|
||||
@@ -35,7 +35,7 @@ A specialized model for generating UI/UX designs, graphics, layouts, and visual
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
# Coming soon
|
||||
response = client.images.generate(
|
||||
|
||||
@@ -35,7 +35,7 @@ A text-to-video generation model producing cinematic-quality video clips from na
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
# Coming soon
|
||||
response = client.videos.generate(
|
||||
|
||||
@@ -37,7 +37,7 @@ A real-time voice synthesis model designed for ultra-low latency live applicatio
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
# Coming soon -- streaming API
|
||||
with client.audio.speech.create(
|
||||
|
||||
@@ -37,7 +37,7 @@ A voice synthesis model for text-to-speech, voice cloning, and multi-language du
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
# Coming soon
|
||||
response = client.audio.speech.create(
|
||||
|
||||
@@ -54,7 +54,7 @@ print(tokenizer.decode(outputs[0], skip_special_tokens=True))
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="zen-eco",
|
||||
|
||||
@@ -45,7 +45,7 @@ curl https://api.hanzo.ai/v1/embeddings \
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="hk-your-api-key")
|
||||
client = Hanzo(api_key="sk-your-api-key")
|
||||
|
||||
response = client.embeddings.create(
|
||||
model="zen-embedding",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user