Commit Graph
773 Commits
Author SHA1 Message Date
zeekay 2a55a3e2c8 regenerate from cloud 3f82fe0da0 2026-08-27 15:19:38 -07:00
zeekay 5a0aa5ee79 name the ref the fleet names, and one that can be fetched
The projection was pinned to a commit that exists only on this machine, so
the receipt named a document no consumer could resolve. The document itself
is byte-identical at both commits -- not one generated line moves -- which is
why this is a one-line correction and not a regeneration.
2026-08-27 13:53:26 -07:00
zeekay 7c4b937533 regenerate from cloud 186c87db6f 2026-08-27 13:52:35 -07:00
zeekay 369b525882 fix(tools): a screenshot arrives downscaled, with the full capture on disk 2026-08-27 08:45:44 -07:00
zeekay 9f6663482d regenerate from cloud 939b09d4
The rebase folded the fleet-wide projection into the newer v8.5.151 already on
main, and two files did not survive it: git_api and websearch_api still
described the previous emission. .spec-lock came back conflicted from the same
rebase and is set to the document these were generated from.

`generate.py python --check` is clean after.
2026-08-24 23:38:24 -07:00
zeekay c32cd7e962 fix(font): the vendored Zen faces named Geist in their name table
The filenames read Zen-*.woff2 and the glyphs are right; only the name
table said family='Geist', designer='Basement.studio, Andres Briganti,
Mateo Zaragoza', vendorURL=basement.studio. Nothing visual can see that —
a font picker, a local() match and a licence audit can.

Replaced with the corrected bytes from hanzoai/font, matched by glyph
content rather than by filename, so the drawing is provably the same face.
2026-08-24 23:33:54 -07:00
zeekayandHanzo Dev dc28d4b761 hanzoai.cloud: project v8.5.151, where the last retired IAM addresses left
.spec-lock named hanzo-inc/cloud@54ddf3d6, whose document still published nine
verb-noun IAM addresses — get-account, get-app-login, delete-mfa,
issue-user-token, mint-user-keys, revoke-user-keys, send-verification-code,
set-preferred-mfa, update-preferences. Every one answers 410 with a Link naming
its successor, so every generated method for them was a call that cannot land.
v8.5.151 (6437e8bb) drops them; measured against api.hanzo.ai/v1/openapi.json it
carries the identical 2253 operations over 1612 paths, zero either way. The only
verb-noun left is /v1/iam/delete-membership, which is served and published.

The smoke tests named four surfaces the pinned document had already stopped
carrying, and were failing before this change: KeysApi (the keys tag folded into
iam), ModelsApi (/v1/models no longer declares security: [], while
/v1/openapi.json has carried the marking across every pin), analytics and chat,
and admin plugins (/v1/tools/plugins is where the published plugin surface is).
Each now names what the document holds, and the breadth floor counts tags rather
than tracking a number that moves with every re-tag.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-24 03:42:28 -07:00
zeekayandHanzo Dev d6ecee069c iam: pass the scope through, and let the server's refusal speak
Which list routes insist on an owner is the server's rule, and it moves. The
deployed binary requires one on users, sessions and applications and not on
roles, permissions, providers, workspaces, invitations, tokens or audit-logs;
the change that resolves a listing's tenant centrally is written but unreleased.

So nothing here keeps a copy of that rule. The IAM tool no longer refuses a
missing owner on applications and sessions, get_applications takes an owner
rather than demanding one, and no docstring or parameter description promises
that leaving it off makes the credential decide — today that answers 400.

A refusal now reaches the caller as the reason IAM gave rather than as a
transcribed body: the RFC 9457 detail, or the envelope's msg where the route
answers the envelope, which is how memberships refuses.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-24 03:42:08 -07:00
zeekayandHanzo Dev 95e2025681 iam: address the rows IAM serves, and read the shape each route answers
A row is named by owner and name — /v1/iam/users/hanzo/z — read with GET,
replaced with PUT, removed with DELETE. The hand-written clients reached for
verb-noun addresses, and the CLI and route table reached for a verb hanging off
the collection; both spellings are gone from the server.

A collection GET answers a wrapper keyed by the plural, and the key is not
always the word in the path — audit-logs answers auditLogs. Every reader now
takes the key from the table and raises when it is absent, because reading a
missing key as [] reports "none" for a healthy org. A row answers the record
itself, absence is 404, and a refusal is an RFC 9457 problem document carrying
the reason. account, memberships, password and login keep the {status, msg,
data} envelope and have their own reader.

The credential goes in Authorization as client_secret_basic, which is where IAM
reads one; the pair used to ride the query string, where it authenticated
nothing and landed in every log on the path. A password rides beside the user
row rather than inside it, so IAM hashes it and nothing writes it in the clear.
An update reads the row before replacing it, since PUT replaces the whole
record.

Role membership is the role's own users list, so adding and removing a member
reads the role, edits the list and writes it back.

The IAM tool drops enforce, system_info and health; the team tool reaches
IAM's workspaces, memberships, invitations and account instead of deferring to
an address it never had. Both name organizations only when the caller does —
IAM scopes the read, and a hardcoded owner is a refusal for everyone outside it.

The CLI route table is checked against IAM's own registered routes, and every
address here was verified against the running server.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-24 03:36:48 -07:00
zeekayandHanzo Dev a43b78eaaf point two examples at the addresses their APIs moved to
`hello` called KeysApi.get_keys(). There is no /v1/keys — API keys are one noun
at /v1/account/keys, so the call is AccountApi.get_account_keys().

`store` called KvApi.post_kv/get_kv_by_name/delete_kv_by_name. /v1/kv is the KV
DATA plane now, addressed by bucket and key; provisioning a KV instance — which
is what this example does — is /v1/provisioning/kv. Same ProvisionRequest, same
three steps, so it is a rename rather than a rewrite.

Both were caught by the examples gate and neither had been true for days, which
is why this repo's pipeline has been red since 2026-08-21 and no regenerated
client has published since.

examples.models is untouched: /v1/models is published and answers 200, and its
operationId is get_models, so ModelsApi appears once the client is regenerated —
which the pipeline does before it tests.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-23 16:10:25 -07:00
zeekay 5ddeca6b5a docs: self-host Zen in place of Geist over the jsDelivr CDN 2026-08-23 15:13:57 -07:00
zeekay 53cd11b0a5 hanzo-iam: one decoder, so a wrong path names itself
hanzo.id serves its sign-in SPA on every unmatched path, so a path IAM
does not serve answers 200 text/html. Forty-four call sites went straight
to .json() on the strength of the status code and died inside it with a
byte offset naming neither the path nor the reason. oauth was the one
module that asked, in a guard of its own.

routes.decode is that question, once, beside the unwrap that answers the
envelope one: the wire decision, then the envelope decision, each with a
name. A test walks the syntax tree and fails if any module decodes a body
on its own.

141 pass across the package and the two consumers that build on it.
2026-08-23 12:57:04 -07:00
zeekay 28035b6721 hanzo-iam: one client, one env reader, one verifier
The credential rode in the query string on AsyncIAMClient too — the
exported one — across seventeen call sites. Same fix as the sync client:
Authorization, Basic for a confidential client.

AsyncIAMClient was DEFINED TWICE, in client.py and async_client.py, and
the copies had drifted to nineteen methods against thirty-eight. __init__
exports the one in async_client.py, so the other was unreachable; it is
gone.

Three readers of IAM_* disagreed about the tenant: IAMConfig.from_env and
fastapi.configure both defaulted the org to the literal "hanzo", so a Lux
or Zoo process that forgot IAM_ORG did not fail — it addressed the hanzo
tenant and reported hanzo's users as its own. Now one reader,
IAMConfig.from_env(org), which seeds the endpoint and the tenant from the
org it is given; organization has no default at all. A test walks the
package and asserts config.py is the only module that reads the
environment.

IAMClient.validate_token and validate_token_with_cert were a second,
weaker verifier — no issuer check, no require:["exp"]. tokens.verify does
both and fails closed, so the strict one is what survives.

118 pass, and the seventeen tests of the two consumers that build on it.
2026-08-23 12:51:30 -07:00
zeekay aa18787d58 hanzo iam: address the routes IAM serves
Every command in this tree called /api/get-users, /api/set-password,
/api/add-provider and twenty-five more — the Casdoor-era surface. IAM
serves /v1/iam/<entity> and /v1/iam/password; all twenty-eight 404d. The
client id and secret rode in the query string besides, where the server
never looks and every log does.

IAM's entities all have the same five routes, so this is one
implementation of them over a table of fourteen entities rather than
fourteen hand-written command trees: 1391 lines to 300, and every entity
gains the verbs only some of them had. The credential goes in
Authorization — Basic for a confidential client, a bearer when one is
exported — and configure writes ~/.hanzo/auth.json 0600.

test_iam_cli reads IAM's own route registrations out of its Go source and
asserts every route this CLI names is one of them: 70 checked, and it is
what caught certs/get answering GET where the table said POST.
2026-08-23 12:46:01 -07:00
zeekay f526911603 hanzo-memory: the vector store is local, not a tracked artifact
343MB over 8,197 files of a developer's own LanceDB — chat_sessions,
memories_test_user_123, facts_test_kb, and embedded markdown — rode in
every clone of a public repo. Untracked and gitignored; the directory
stays where it is on disk.
2026-08-23 12:34:47 -07:00
zeekay 02f2ec2014 hanzo-mcp: the README documents the server that exists
The quickstart called create_mcp_server, server.register_*_tools and
server.tools.read — no such names. The real surface is the hanzo-mcp
command, its flags, and HanzoMCPServer; the tools come from whichever
hanzo-tools-* packages are installed, through the hanzo.tools entry
point. Verified against --version, --help and the constructor.

Drop the three *_COMPLETE.md status reports, MODULAR_ARCHITECTURE.md
(a proposal), COMPREHENSIVE_DOCUMENTATION.md (claims 70+ tools over a
package with seven) and SEARCH_GUIDE.md (documents an ffind tool that
does not exist).

SECURITY_PATCH.md named files and lines for three findings in 0.8.13;
all three are addressed in 0.15.15 — the shell escaper it flagged is
gone with its module, permissions.py refuses .. and ~ before resolving,
and the third asked for a token the server minted, persisted to
~/.hanzo/mcp_token and never once checked. That token is gone too: auth
is IAM's, and a credential nothing verifies reads as a gate that is not
there.
2026-08-23 12:31:08 -07:00
zeekay 5bc4b34731 iam: the credential rides in Authorization, not the query string
hanzo-iam's admin calls sent clientId/clientSecret as query parameters.
IAM's app() reads httpx.Basic(c) — the Authorization header and nothing
else (internal/authz/authz.go) — so the pair authenticated nothing and
landed in every access log, proxy log and Referer on the path.

_admin_headers now renders it as HTTP Basic (RFC 6749 2.3.1), a bearer
still wins where one is held, and _admin_params is gone rather than left
returning an empty dict. The async class gets the same one helper.

test_credential_location pins it on both arms: put the pair back in the
query and the test fails on the string it leaks.
2026-08-23 12:23:26 -07:00
zeekay c6a60aea35 docs: cut the explanatory prose from the README; drop the stale changelog and boilerplate contributing guide 2026-08-23 12:12:51 -07:00
zeekay 311bdfc7c9 The install line named a version five majors old
`pip install hanzoai==3.2.13` — PyPI serves 8.5.89. Anyone following the first
command on the page pinned themselves to a client from before most of the API
existed, and a pinned version in prose is stale the day after the next release.
It names no version now.

The note beneath it explaining that 3.2.1 and earlier used `get_v1_keys` rather
than `get_keys` goes with it: every published version is long past that rename,
so it described a client nobody can install by accident.
2026-08-23 11:27:50 -07:00
zeekay 57b6b6319e Drop the inventory counts from the README
It opened by telling a developer the client covers so many operations over so
many paths in so many services, and repeated variations of that further down. The
number is not something a reader can act on, it was wrong — the document had
moved on — and keeping it right means measuring the document on every build to
maintain a sentence nobody needed.

The prose says what the client IS. Where a count explained real behaviour — some
operations declare no response body, so their method hands back nothing to
decode — the explanation stays and the tally goes.
2026-08-23 11:05:51 -07:00
zeekay 3f9f455a25 The README states the size of the client it actually is
It opened with "2,479 operations over 1,814 paths, grouped into 192 services".
The document serves 2,259 over 1,620 in 117 — every number wrong, in the first
paragraph a developer reads. They were true once and the document moved.

The count is now stamped by the generator from the spec each build was cut from,
inside a `<!--counts-->` span, so it cannot drift again. Any derived figures
beside it were re-measured against the same document rather than carried over.
2026-08-23 10:40:18 -07:00
zeekay 791a13a0ed Regenerate against the current API document
74 commits of cloud had landed since these clients were last cut: +7 operations,
-2. The additions include GetBaseBases and GetBaseBasesByOrg — the listing that
answers which Bases a caller can reach, which is why a workspace built on this
client could not show one.

.spec-lock moves with the code. It named the older document while the tree was
regenerated from the newer one, and a lock that disagrees with the files beside
it is worse than no lock: it is what the next reader checks to decide whether
they are behind.
2026-08-23 10:01:23 -07:00
zeekay 841130f02e Regenerate against the released document (v8.5.102)
Projected from hanzo-inc/cloud@59ec4deb4 openapi.yaml, sha256
e7e90d5445aeccfe332d942d0edd7a77df14336d97a445b71fb2f3047db65192.

By hand: the CI lane clones the private driver with a KMS-minted IAM machine
token, and that subject is linked to no account on git.hanzo.ai, so it verifies
and authorizes nothing.
2026-08-22 08:01:30 -07:00
zeekay be1e2814c4 name the neighbour, so the wrong one is not installed
hanzo-agent and hanzo-agents differ by one letter and are different products:
one is an SDK for building agents, this is a CLI for driving them. They share no
code and neither depends on the other, which is exactly why the names mislead.
Each README now says which is which.
2026-08-21 16:21:43 +00:00
zeekay a0ee31e20f name the neighbour in the docstring too
Same one-letter confusion as the README: hanzo-agent is the SDK, hanzo-agents is
this CLI. A reader who reaches the module before the README should still be told.
2026-08-21 16:21:42 +00:00
zeekay 9d7794d81c name the neighbour, so the wrong one is not installed
hanzo-agent and hanzo-agents differ by one letter and are different products:
one is an SDK for building agents, this is a CLI for driving them. They share no
code and neither depends on the other, which is exactly why the names mislead.
Each README now says which is which.
2026-08-21 16:21:30 +00:00
zeekayandHanzo Dev 2063e57090 the docs theme gets the icon it asks for
mkdocs.yml names assets/favicon.svg beside assets/logo.svg. Only the logo was
committed, so every build of the hanzo-agent docs resolved the favicon to
nothing and served the default.

The publish error also offered "a forge" as a place to put a PyPI token. It
means git.hanzo.ai; say that.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-20 19:53:51 -07:00
zeekayandHanzo Dev d301b245e2 follow the release pointer the rest of the fleet follows
This repo pinned an exact hanzoai/ci release, so it kept running the build.yml
that reads git.hanzo.ai through HANZO_GIT_TOKEN — a credential no org seals.
The gate has been red on that ever since, while the sixty-four repos on @v1
moved past it.

@v1 is not an unproven tip. hanzoai/ci pins its own gate to the last release and
moves that pin first, so a release proves itself there before @v1 follows it.
An exact pin here opts out of that and rots instead.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-20 19:53:49 -07:00
zeekay 93af3004c6 track the name agents read, so a clone documents itself
LLM.md is the source and stays the source; CLAUDE.md is a symlink to it, so there is
one file and it cannot drift from itself. What changes is that the link is committed
rather than left for each checkout to recreate.

Left untracked, the doc was conditional on a step outside the repo: a fresh clone had
no CLAUDE.md, nothing loaded LLM.md, and an agent worked here having never read what
this repo already explains. Most of the estate already tracks theirs; this is the rest
agreeing with them.
2026-08-20 16:29:10 -07:00
zeekayandHanzo Dev e1b48fe3c1 release: 8.5.89 — the V8 launch version
Co-authored-by: Hanzo Dev <[email protected]>
v8.5.89
2026-08-20 14:13:04 -07:00
zeekayandHanzo Dev accd79d1c4 release: 8.5.89 — the V8 launch version
Hanzo 8 is one product on one number. The API, the six generated clients, the
CLI and the console all answer to 8.5.89, so a client version names the API it
was cut from without a compatibility table.

8.5.89 is the first cut past the highest 8.x anyone had reached (@hanzo/console
8.5.88). Every register moves FORWARD into it; nothing is renumbered backwards.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-20 14:08:18 -07:00
zeekayandHanzo Dev b3483fba72 cicd: carry the release's version too, at ci v1.0.77
The ref said which document; nothing said which version it is. A projection of
one document answers to that document's number, so the poke carries it and the
cut uses it verbatim instead of counting this repo's own patches.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-20 11:56:06 -07:00
zeekayandHanzo Dev 78f4af1e1f release: 3.2.14 — the client regenerated onto cloud@dd98c965
Co-authored-by: Hanzo Dev <[email protected]>
v3.2.14
2026-08-20 10:25:44 -07:00
zeekayandHanzo Dev f06d33b3a9 spec: regenerate onto cloud@dd98c965
The committed client came from a document 262 commits old. Regenerated with the
lane own inputs -- scripts/generate.sh, the spec at a pinned ref, the driver
checkout -- so this is the derivation CI does, not a second one.

The surface moves the way the document moved: the datastore, docdb, kv and sql
clients collapse into instances (those routes re-homed under /v1/instances),
allowance arrives, sentry becomes sentinel, and the text-to-speech,
speech-to-text and health clients go because cloud no longer serves them.

No version bump. Publishing is a decision, and this commit is only about the
client being true.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-20 09:46:20 -07:00
zeekayandHanzo Dev f50ef05473 cicd: ci v1.0.76 — the git.hanzo.ai reads take their own credential
v1.0.75 read the document with github.token, the per-job token, which is scoped
to the repo running the job and answers 404 for a private repo in another org.
Both reads in that lane cross one. The 404 read as a missing document rather
than a refused credential.

v1.0.76 gives those reads their own KMS-sealed HANZO_GIT_TOKEN and says so by
name when it is absent, so the next failure names its own remedy.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-19 23:20:38 -07:00
zeekayandHanzo Dev 7b80afeb4a hanzo.yml: name the cloud this SDK is a projection of
hanzoai/ci defaults client.spec.repo to hanzoai/cloud, and that repo is now the
re-rooted OSS core -- a short line sharing no ancestor with the product. The
ref this projection is pinned to does not exist there, so every run of the
client lane, check or move, died fetching it: `curl: (22) ... 404`.

Measured: the same token reads openapi.yaml at 113cf7d6 from hanzo-inc/cloud
with a 200 and from hanzoai/cloud with a 404. Nothing about the credential.

`.spec-lock`'s own repo= line does not set this -- ci reads the repo from
hanzo.yml and uses the lock's copy for a commit message -- so the two could
disagree in silence, and did. They are one fact and now agree.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-19 22:57:53 -07:00
zeekayandHanzo Dev 208240ecff cicd: the poke carries the ref, so a release can actually move the pin
This file claimed a bare dispatch took cloud's newest tag. ci v1.0.54 removed
that path, so what a poke really did was re-read the LOCKED ref, assert its
digest and exit before any write -- a check wearing a regeneration's clothes.
Measured: this projection sat 254 commits behind cloud while PyPI matched its
newest tag exactly, because publishing was never the broken half.

The ref now travels with the poke, as ci v1.0.75's spec-ref/spec-sha256 inputs.
That is the same fact repository_dispatch carried as client_payload.sha, spelled
for the one dispatch verb Hanzo Git serves. Omit both and the lane is a check
that writes nothing, exactly as today.

ci moves v1.0.54 -> v1.0.75 because the input does not exist before it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-19 22:55:25 -07:00
zeekay 8aa0e49c3b say Hanzo Git, which is what it is
"Forge" named nothing in this estate. The product is Hanzo Git, the address is
git.hanzo.ai, and the in-cluster service is hanzo-git.hanzo.svc. A generic word
invented for it sends a reader looking for a system that does not exist.

Comments only. ("unforgeable" stays — different word.)
2026-08-19 22:28:08 -07:00
zeekayandHanzo Dev 67304c3c12 the lock names the repo that holds the document
Every SDK locked its spec to hanzoai/cloud. That repo is a 12-commit stub with
no openapi.yaml in it at all, so the raw fetch answered 404 and the client lane
has failed on every push since. The document is in hanzo-inc/cloud — 10,618
commits, where the locked ref resolves and openapi.yaml at it hashes to exactly
the sha256 this lock already names.

Naming the real location does not by itself restore the lane: that repo is
private, and the hanzoai token reads it as not-found. But a lock pointing at a
repo with no document can never work, and this one works the moment the token
can see it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-19 21:36:25 -07:00
zooqueen 95cf0799f9 the browser transport is the zapd consumer, and only that
hanzo_mcp.zap_server imported hanzo_tools.browser.zap_server, which does not
exist — the module could not be imported at all. Its caller swallowed that in
`except ImportError: return`, so _start_zap_server() ran on every `run()` and
started nothing, and the HANZO_NO_ZAP switch guarded a path that never had an
effect either way.

The browser tool reaches Chrome through the native host -> zapd -> the socket
at ~/.zap/run/zapd.sock, where hanzo-mcp is a consumer (hanzo_tools.browser
.zapd_consumer). That is the transport, so the WebSocket server and its mDNS
discovery go, along with _normalize_mcp_result, which existed to shape results
for that wire and had no other caller.
hanzo-mcp-v0.15.15
2026-08-18 23:13:53 -07:00
zeekayandHanzo Dev 22c0e467a3 cli: drop 'hanzo install runtime'
hanzoai/runtime is archived. The subcommand set up a checkout at
~/work/hanzo/runtime and installed the hanzo-runtime / @hanzo/runtime
SDKs — a source tree that no longer exists and packages that were never
really released (npm only ever had 0.0.0-dev; PyPI's last release was
2025-07-15).

Verified: hanzo.commands.install imports clean and install_group now
exposes 13 subcommands with 'runtime' absent.

(cherry picked from commit 16d31994dd)

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-15 09:07:02 -07:00
zeekay ae735c868b the manifest the driver writes is a tracked file
.generated names every path generate.py last wrote here. CI clones fresh, so a
manifest that exists only on the machine that ran the generator leaves the
driver unable to tell what it owns — and a file that stops being generated
stops being removed.

The four other clients already track it; this one did not.
2026-08-14 22:03:58 -07:00
zeekay f3303e4119 the tag lane's other fault: a ref the Actions user pushed starts nothing 2026-08-14 18:22:42 -07:00
zeekay 0f5676c200 one release path, and the docs say the one that runs
RELEASE.md described GitHub Actions, GitHub secrets and three bin/ scripts
that each took a PyPI token off the environment. None of that publishes this
repo — .hanzo/workflows/publish-pypi.yml does, reading the token from KMS —
so the scripts are gone and the page describes the lane, its tag mapping, and
what to check before a tag makes a version permanent.

LLM.md carries the published state: hanzoai 3.2.13 with get_keys/get_tools,
and the KMS route that a 404 will otherwise misreport as a missing secret.
2026-08-14 17:51:02 -07:00
zeekay 80c31f3534 publish-pypi: read KMS at the route KMS serves
The job asked for /v1/kms/orgs/hanzo/secrets/<path>/<key> and parsed
.secret.value. KMS serves GET /v1/kms/secrets/<path>/<key>?env=<env> and
answers {"env","name","value"}, taking the org from the token's own claim,
so every read 404'd and every release reported the credential unseeded —
including hanzo/prod/python-sdk-publish/PYPI_TOKEN, which is there.

Root to 3.2.13, README install line names it. hanzo-research declares its
readme, license and authors, so its page carries them.
v3.2.13
2026-08-14 17:42:58 -07:00
zeekay def463a5ac uv.lock: carry the 3.2.12 version v3.2.12 2026-08-14 17:22:45 -07:00
zeekay 4998a916fd the client sends the credential the document now declares
hanzoai/cloud@113cf7d6 declares components.securitySchemes.bearer and applies it
document-wide, so regenerating writes a populated Configuration.auth_settings()
and auth_settings=['bearer'] into 2498 call sites; ApiClient._apply_auth_params
sets Authorization: Bearer <token> from access_token, which was read by nothing
before. Four call sites carry an empty list — exactly the operations the document
marks security: [] (get_models, get_models_providers, get_commands,
get_openapi_json) — so the public routes still go out with no credential.

examples/client.py drops the header_name/header_value compensation and passes
access_token. examples/models is a new flow on the public catalog: it runs before
a reader has a key, which makes the install checkable in one command.

Proven on api.hanzo.ai, not by reading: examples.models prints 112 models with no
credential; examples.hello returns the caller's keys with a real sk- and 403
"sign in to manage API keys" with a bogus one.
2026-08-14 17:22:30 -07:00
zeekay b739f2ab6e hanzo-network: carry exo's GPL-3.0 and attribute the fork
The distributed inference, topology and device-capability code derives
from exo (github.com/exo-explore/exo), which was GPL-3.0 when this fork
was taken. The package declared BSD-3-Clause, which grants terms the
upstream licence does not.

Ship exo's GPL-3.0 text, add a NOTICE naming upstream and recording the
modifications, and declare GPL-3.0-only. hanzo-network stays an optional
extra with guarded imports, so the base CLI is unaffected.
hanzo-network-v0.1.4
2026-08-14 16:55:05 -07:00
zeekay bcbea8ce31 drop a script that existed to hold a key
openai_api_check.py was not a test — pytest never collected it, nothing imported
it, and its whole body was a hand-run connectivity probe against a vendor with
the credential written in as a literal. There is nothing to keep.

The value it carried must still be rotated: it is in this repo's history and the
history is public.
2026-08-14 16:52:07 -07:00
zeekay ef4f774da5 the credential comes from the environment
This script carried its key as a literal. A key in a source file is a key in
every clone of that file, and this repo mirrors publicly, so the value must be
rotated independently of this change — removing it from the tip does not remove
it from history.

Reads OPENROUTER_API_KEY and exits with a clear message when it is unset.
2026-08-14 16:51:14 -07:00