Documentation
¶
Overview ¶
client.go is the ONE HTTP path from this subsystem to Visor (the cloud OS at visor.hanzo.svc:19000 that owns compute — machines and DOKS node pools). Every handler in visor.go routes through this client, so the wire contract (base URL, auth, the casibase {status,msg,data} envelope, error mapping) lives once here and can never drift between six hand-rolled fetches.
AUTH (one rule): a request carries a Visor identity that is EITHER the service credential (VISOR_CLIENT_ID + VISOR_CLIENT_SECRET, KMS-sourced, sent as Basic auth so Visor's ApiFilter authorizes cloud as the `app/<visorApp>` subject) OR the caller's forwarded Authorization bearer when no service credential is configured. The tenant is ALWAYS pinned by ?owner=<org> (the validated principal's org, never a client field) plus the forwarded identity headers, so Visor scopes to exactly the caller's tenant on both paths.
ENVELOPE: Visor (casibase) returns HTTP 200 with {status:"ok"|"error", msg, data}. A logical failure is status:"error" at HTTP 200, NOT a 4xx/5xx — so a bare status-code check would read an error as success. call() inspects the status field and surfaces msg as an honest error; it never fabricates data.
types.go holds the Visor wire structs (what upstream returns) and the console view structs (what this subsystem emits), plus the PURE mapping between them. The view JSON keys mirror the console normalizers EXACTLY so the Machines, GPUs and Clusters pages render with no front-end change:
- machineView -> console2 src/lib/api/visor.ts normalizeMachine
- gpuView -> console2 src/lib/api/compute.ts normalizeGpu
- clusterView -> console2 src/lib/api/platform.ts Cluster + NodePool
Every field is a REAL Visor value or an honest omission. Telemetry Visor does not carry (GPU utilization/temperature/power) is left off the gpuView so the UI shows "—", never a fabricated 0.
Package visor mounts the Hanzo Cloud COMPUTE surface: the tenant's machines, GPUs and DOKS clusters, served as clean REST off the unified cloud binary and fronting Visor (the cloud OS at visor.hanzo.svc that OWNS compute). It exists so the console's Machines / GPUs / Clusters pages read real per-org compute from ONE place (api.hanzo.ai/v1/*) instead of the god-mode /paas admin proxy that 501s until a service token is wired.
This subsystem OWNS no compute state — Visor does. It is a thin, tenant-scoped translator: it maps Visor's verb-style + resell endpoints to the clean REST the console already speaks, and re-shapes Visor's objects into the exact JSON the console normalizers consume (see types.go). It never fabricates: a GPU row is a real GPU machine's accelerator, a cluster is real node pools, and telemetry Visor does not carry is honestly omitted (renders "—"), not invented.
Surface (every route org-scoped by the validated principal; HIP-0026):
GET /v1/machines list the org's machines -> {machines:[machineView]}
POST /v1/machines launch (or dryRun quote) -> machineView | quote
GET /v1/machines/:id one machine by name -> machineView (404 if absent)
DELETE /v1/machines/:id terminate a machine -> 204
GET /v1/gpus per-accelerator inventory -> {gpus:[gpuView]}
GET /v1/gpus/alerts GPU alerts (honest empty) -> {alerts:[]}
GET /v1/clusters DOKS clusters (from pools) -> {clusters:[clusterView]}
POST /v1/clusters/:clusterId/pools add a node pool -> nodePoolView
POST /v1/clusters/:clusterId/pools/:poolId/scale scale a node pool -> nodePoolView
DELETE /v1/clusters/:clusterId/pools/:poolId delete a node pool -> 204
The tenant (principal.Tenant) is passed to Visor as ?owner=<org>, so a caller can only ever read or mutate their OWN tenant's compute; the org is taken from the validated IAM owner claim, never a client field.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
This section is empty.