Commit Graph
8 Commits
Author SHA1 Message Date
zeekayandHanzo Dev d08a7d5599 auth: verify tokens instead of asserting they exist
is_authenticated() was `load_token() is not None` — a string-presence check
that returned True for the literal "fake.not.a.real.jwt". Every permission
decision downstream of it was therefore a lie. This makes the client side of
Hanzo auth actually work, end to end. Builds on the /v1/iam prefix already on
main; the endpoint paths were necessary but not sufficient.

hanzo_iam.tokens — the ONE credential judge. Verifies the signature against
the issuer's published JWKS plus exp/iss/aud, and fails CLOSED: an unreachable
JWKS is not a pass, because a client that cannot check a signature does not
know the token is good. Reason codes distinguish "expired" from "offline".
`alg: none` is absent from the accepted list by construction. An opaque API
key cannot be judged offline, so it is confirmed against userinfo rather than
assumed valid for being a non-empty string.

hanzo_iam.oauth — a real login: authorization code + PKCE (S256) over a
loopback redirect, with a deadline. Not the device grant, which would be the
better CLI UX: iam implements RFC 8628 fully and advertises it, but no PUBLIC
client is registered, so POST /v1/iam/oauth/device answers 401 invalid_client
for hanzo-cli, hanzo-app, hanzo-cloud and hanzo-console alike. Registering one
public app is the whole server-side fix; shipping a device path that always
401s would be a lie in code. The password grant is out for the same reason
(401 invalid_client without a secret), so `--no-browser` now prints the URL
instead of prompting for a password it cannot use.

The loopback listener binds a REGISTERED redirect_uri. iam compares
redirect_uri by exact string and does not apply RFC 8252 §7.3 port-agnostic
loopback matching, so a CLI cannot pick a free ephemeral port — the previous
code bound 8399 (cli) and 8398 (bot), neither registered, and /authorize
refused both with a bare 400 before the user saw a login page. It listens on
127.0.0.1 and ::1 because the registered URIs spell "localhost", and sets
SO_REUSEADDR because the previous callback sits in TIME_WAIT for ~60s and
would otherwise EADDRINUSE a second login on a port nothing is using.

hanzo_iam.store — keyring first, else an atomic 0600 file. The old path did
write_text() then chmod(0600), so with umask 022 a bearer token sat at 0644
between the two calls. Now it is created private and renamed into place. The
PaaS session cache goes through the same writer.

Two definitions of IAMConfig existed; models.py's was the one every client
imported, so the endpoint properties main had just fixed on config.py's were
dead code. One now, in config.py, plus jwks_uri and device_endpoint.

The bot and CLI login copies are gone; both call the one flow. whoami and
`hanzo bot login` no longer print claims from an unverified decode — that
rendered an attacker-chosen identity as fact.

The Team tool refuses instead of pretending: api.hanzo.ai/team is the
marketing SPA (200 text/html), so raise_for_status() passed and .json() blew
up. There is no Team API; HANZO_TEAM_URL stays as the seam for when one ships.

PaaS token exchange reports its real diagnosis: POST /v1/auth/login 404s while
/v1/org and /v1/user 401, so the API is up and gated but the exchange route is
not deployed at that edge. No client-side workaround exists, so it says so.

Tests fail against the old behaviour, which is the point: 5 session tests fail
against the shipped is_authenticated and 4 store tests fail against
write-then-chmod. 134 pass alongside main's KMS suite.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 18:55:24 -07:00
zeekay f6c516868c feat(cli,mcp): complete S3 control surface — hanzo s3 + s3 MCP tool
Closes the last gap in the one-way Hanzo control surface. IAM/KMS/PaaS
already had both CLI subcommands and MCP tools; S3 had neither.

- hanzo s3: buckets/mb/rb/ls/stat/rm/presign over hanzo_s3 (MinIO) client,
  creds from env (inject from KMS, never plaintext). Registered in the
  unified hanzo CLI alongside iam/kms/paas.
- hanzo-tools-s3: S3Tool MCP tool (same action-dispatch pattern as PaaSTool),
  exported via the hanzo.tools entry point; s3 added to loader prefix map.
- uv workspace: map hanzo-cli/iam/kms/s3 + tools-s3 to local sources so the
  unified CLI resolves on-disk packages (fixes hanzo_iam ImportError from a
  stale published 1.29.0 wheel shadowing local 1.30.0).

Tests: 7 CLI + 14 MCP, all green.
2026-06-24 13:50:24 -07:00
zooqueenandHanzo Dev b144973bfc fix(lint): resolve all ruff + black errors across all packages
Fix 35 ruff errors (E722, F841, B904, B905, B007, E741, S202, S310,
S306, S110, TID251, UP042, F401) and run black on 526 files.

Co-authored-by: Hanzo Dev <[email protected]>
2026-02-21 23:32:32 -08:00
zooqueen dacfe25597 feat(cli): add bot commands, s3 storage, dev/net passthrough
- Add `hanzo bot` command group (status, logs, deploy, env, events) via PaaS API
- Replace all storage placeholders with real boto3 S3 API calls to s3.hanzo.ai
- Rename `hanzo storage` → `hanzo s3` (storage kept as alias)
- Rewrite `hanzo dev` as lazy-install passthrough to @hanzo/dev binary
- Rewrite `hanzo net`/`hanzo node` as passthrough to hanzod binary
- Remove ~350 lines of inline orchestrator/node code in favor of os.execvp()
2026-02-21 11:54:01 -08:00
zooqueenandHanzo Dev 7d3d318e5e feat(cli): add k8s kubectl wrapper and remove REPL
- Add `hanzo k8s` command group to hanzo-cli that wraps kubectl with
  Hanzo-managed auth and default namespace. Uses KubectlGroup that
  dynamically proxies unknown subcommands to kubectl (e.g.
  `hanzo k8s get pods` -> `kubectl get pods -n hanzo`).
- Remove REPL mode from hanzo wrapper: bare `hanzo` now shows help
  instead of dropping into interactive REPL.
- Remove hanzo-ai, hanzo-chat, hanzo-repl entry points.
- Make hanzo-cli a core dependency of hanzo (was optional [cli] extra).
- Remove unused typer dep, move openai/anthropic/prompt-toolkit to
  optional extras ([ai], [interactive]).

Co-authored-by: Hanzo Dev <[email protected]>
2026-02-21 09:46:50 -08:00
zooqueenandHanzo Dev 73fcb87ad9 feat(cli): add top-level deploy alias and fix KMS create_secret
- Add `hanzo deploy` as top-level alias for `hanzo paas deploy`
- Fix KMS create_secret sending null secretComment (caused 422 errors)
- Login callback page styling (from previous session)

Co-authored-by: Hanzo Dev <[email protected]>
2026-02-20 22:13:04 -08:00
zooqueenandHanzo Dev 920f767417 style: auto-fix ruff lint and format for Python 3.12
Run ruff check --fix and ruff format across the repo to fix ~495
auto-fixable lint errors (mostly I001 import sorting) and reformat
225 files to match the py312 target.

Co-authored-by: Hanzo Dev <[email protected]>
2026-02-20 21:29:31 -08:00
zooqueenandHanzo Dev 5276a58d19 feat: add hanzo-cli unified CLI with IAM, KMS, PaaS management
- New hanzo-cli package (0.1.0) providing the `hanzo` command
- Auth: browser OAuth + ROPC password login against hanzo.id (Casdoor)
- IAM subcommands: users, user, set-password, orgs, apps, sync-app
- KMS subcommands: list, get, set, delete, inject
- PaaS subcommands: orgs, projects, envs, deploy (create/status/logs/redeploy/env/delete)
- PaaS deploy supports both Docker images (--image) and git repos (--repo)
- Sticky org/project/env context for PaaS commands
- PaaS client with IAM→PaaS session exchange and auto-refresh
- hanzo-iam SDK v1.1.0: bearer_token auth, admin helpers, set_password/get_applications/update_application

Co-authored-by: Hanzo Dev <[email protected]>
2026-02-18 20:58:43 -08:00