cloud

package module
v1.801.463 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 89 Imported by: 0

README

Hanzo Cloud

Hanzo Cloud

The Open AI Cloud as one deployment. Identity, secrets, data, AI, gateway, observability, and the console — 116 Hanzo-native subsystems behind one origin and one /v1, each its own binary, composed by a light host router through the plugin contract in HIP-0106.

Status License

The same artifact serves api.hanzo.ai, api.lux.cloud, api.zoo.cloud, api.osage.cloud, and every white-label reseller. Brand, enabled subsystems, and org scope are deployment configuration — one binary, one origin, no sidecars.

Quick start

# Run the unified binary. `:latest` to try it; pin a v1.x.y tag for anything real
# — the tags are cut per build, so any number written here is stale by tomorrow.
docker run -p 8080:8080 ghcr.io/hanzoai/cloud:latest

Open http://localhost:8080 for the embedded console; the API is served under /v1 on the same origin.

Build this repo's own client binary with go build ./cmd/hanzo — see below for what it serves and what it delegates. It is NOT what curl -fsSL https://hanzo.sh | sh installs; that gets the Rust CLI (hanzoai/cli), which is the primary hanzo on a developer's machine and whose verbs are different.

What this is

hanzoai/cloud serves the whole API from one origin. cmd/cloud is the front door: it links zip, the app manifest and the console embed — and nothing else. It knows only where each app lives and what path it answers, never what the app does. Each subsystem (iam, kms, base, gateway, ai, commerce, vfs, mq, dns, amqp, mcp, o11y, tasks, …) is its own plugin/<name> binary serving its own prefixes through the same cloud.Listen middleware it would serve standalone.

Apps start lazily, on the first request that reaches their prefix; the four that own a listener or a background loop (pubsub, kafka, o11y, catalogsync) say so and start with the host. That is what makes 116 subsystems affordable — an app nobody calls costs a route entry and a struct, not a process and a resident set.

This was one fused process once, and that binary is gone: it linked every subsystem's graph into a ~3105-package build, and apps.Wire() went with it.

The same deployment serves api.hanzo.ai, api.osage.cloud, api.lux.cloud, api.zoo.cloud, and every white-label reseller. Brand, enabled subsystems, and org scope are deployment configuration.

hanzo — cloud control CLI

cmd/hanzo is the client-only control binary: a thin client over Hanzo IAM (hanzo.id), the platform control plane (platform.hanzo.ai/v1) and the cloud /v1 API, inventing no parallel API. It cannot serve a subsystem — that is cmd/cloud's job.

Two different programs answer to hanzo, and this is the one almost nobody has. A developer installs the Rust CLI (hanzoai/cli) from hanzo.sh; it becomes their hanzo, and it writes hanzo-node as a symlink to itself. THIS binary is the Go control CLI, built from this repo. When it is the hanzo on a machine, a verb it does not own is handed to whatever hanzo-node resolves to (cli.Passthrough), so the single name is a superset of both — but that delegation runs in this direction only. Read the verbs below as cmd/hanzo's, not as "what hanzo does": on a normal developer machine hanzo login and hanzo deploy reach the Rust CLI, which has neither, and it reads them as a task for the coding agent.

cli.IsControlVerb draws the line off the cobra command tree itself, so the router and the tree cannot drift apart. The complete set it owns:

hanzo login                       # IAM password grant against hanzo.id → token in ~/.hanzo (0600)
hanzo logout
hanzo whoami                      # identity from the stored token (--verify hits IAM userinfo)
hanzo auth …                      # token / switch / status
hanzo apps list                   # platform apps board: declared/running/latest tag + drift + health
hanzo apps get <org>/<app>/<env>  # one app row
hanzo deploy <container> --project <p> --env <e>   # rolling, zero-downtime redeploy
hanzo clusters …                  # dedicated DOKS cluster lifecycle
hanzo build <repo> --sha <sha> --image <img>       # platform-native (arcd/Kaniko) build, no GitHub builders
hanzo run <task>                  # one-off task on the platform
hanzo agent … | hanzo bot …       # managed agents and bot nodes
hanzo engine … | hanzo runner …   # local engine, and this machine as a CI runner
hanzo link | hanzo unlink         # attach this machine to the fleet (`hanzo gpu connect` rides here)
hanzo security …                  # rules / scan
hanzo config set <k> <v>          # ~/.hanzo/config preferences
hanzo version
hanzo completion bash|zsh|fish    # shell completion for every verb above

Global flags: --org, -o/--output table|json, --platform-url, --iam-issuer, --platform-token. Tokens resolve from flag → env → ~/.hanzo (never hardcoded): the IAM user token is the identity; the platform control plane is service-token authed (it cannot validate user tokens), so apps/deploy/clusters use --platform-token / HANZO_PLATFORM_TOKEN / PLATFORM_SERVICE_TOKEN, and build uses HANZO_BUILD_TOKEN / PLATFORM_BUILD_CALLBACK_TOKEN.

Install the Rust CLI: curl -fsSL https://hanzo.sh | sh, or brew install hanzoai/tap/hanzo. It is hanzoai/cli; this module serves /v1, ships plugins, and builds the control half above (go build ./cmd/hanzo).

Subsystems mounted

manifest/apps.go is the source of truth: every app that ships as its own binary, in mount order — which IS the routing order, first matching prefix wins. Three facts per row and no more (name, the paths it answers, whether it must already be running), because that is the whole of what the light host needs to know. What an app DOES it states once in its own plugin/<name>/main.go.

  • iam — identity & access (users, orgs, roles, OIDC/JWKS per HIP-0026)
  • base — per-org SQLite + in-process extension runtimes (HIP-0105)
  • kms — secret custody (sealed secrets, HIP-0027)
  • commerce — checkout, billing, pricing, invoicing (light router; NOT in PCI-DSS scope)
  • ai — AI control plane: inference, RAG, model hub, agents, MCP management
  • gateway — HTTP routing + policy
  • o11y — metrics / traces / logs
  • vfs — virtual filesystem / object-store abstraction
  • mq — message queue
  • dns, amqp, mcp, auto, tasks, … — the other 107 rows are in manifest/apps.go

Deployment modes

Same artifact; different startup configuration:

cloud --brand=hanzo  --domain=hanzo.ai
cloud --brand=osage  --domain=osage.cloud
cloud --brand=lux    --domain=lux.cloud
cloud --brand=zoo    --domain=zoo.cloud

Architecture

                 api.{org}.{brand}
                          |
              cmd/cloud — the host router
              (links zip + manifest + webui, nothing else)
                          |
   +----------+----------+----------+----------+----------+
   |    iam   |   base   |   kms    |    ai    | gateway  | ...
   |  its own |  its own |  its own |  its own |  its own |
   |  process |  process |  process |  process |  process |
   +----------+----------+----------+----------+----------+
   per-org SQLite (HIP-0302)   |   Hanzo IAM JWKS (HIP-0026)
   replicate -> S3 (HIP-0107)  |   ZAP inter-subsystem RPC

Every app is loaded through the same Mount seam and answers on its own prefix; the host takes the first prefix that matches and starts the app if it is not up yet. The console is registered LAST so no app prefix can be shadowed. Cross-subsystem calls ride ZAP; no subsystem reaches into another's store.

The host owns three things no app can: it serves the white-labelled console at /, it threads the deployment's operator flags to the children as CLOUD_* env, and it SCOPES CREDENTIALS — it scrubs the KMS root key from its own environment so no child inherits it, and hands it to the kms broker child alone.

White-label fork pattern

Customers fork hanzoai/cloud to launch their own ecosystem. Brand detection, enabled subsystems, and ZAP endpoints (payments / vault backends) are all deployment configuration.

Web framework

zap-proto/zip — Sinatra-style Go web framework built on Fiber v3. The ONE Go web framework. No .Fast escape hatch. That is the module path this repo imports (github.com/zap-proto/zip, currently v1.18.22); hanzoai/zip is the old home and is not what go.mod resolves.

Console UI — embedded in the host

The host binary serves the console (@hanzo/gui, hanzoai/console — private) UI at the web root AND routes /v1 — one origin, no separate console Service. The UI is compiled in via //go:embed (see webui.go).

Pipeline (in the Dockerfile, before go build):

console stage  →  build console static bundle  →  /out
      COPY --from=console /out/ → src/webui/dist/     (overlays the fallback shell)
build stage    →  go build   →  //go:embed all:webui/dist bakes it into /cloud

Serving (webui.go, registered LAST in Serve so it never shadows the API):

  • GET / and any client-side route (/orgs, /models, …) → the SPA shell (index.html) with Cache-Control: no-cache; fingerprinted assets under assets//_next/ are served immutable for a year, with brotli/gzip precompressed negotiation when the build emits .br/.gz siblings.
  • GET /v1/* (and /zap, /healthz, …) → the API. Real subsystem routes are registered before the console catch-all, so they always win; an unmatched path under an API prefix returns a real 404 (JSON namespace), never HTML.
  • Same-origin: the embedded console calls /v1 on its own host, so the session cookie is first-party — no second origin, no CORS.

webui/dist/index.html is a committed fallback shell (a real same-origin /v1 bootstrap) so go build always compiles and the binary always serves a UI even without the Node toolchain. The image build overwrites webui/dist with the real console bundle. See webui_test.go for the boot-and-assert tests (/ → shell, deep link → shell 200, /v1/* → API, unmatched /v1 → 404).

The hanzoai/console build:embed script (scripts/build-embed.mjs) stashes its Next server route handlers (BFF proxies that collapse to the cloud /v1/* the SPA calls same-origin), wraps the client catch-all pages for output: 'export', neutralizes the root layout's request-time headers() read, and emits a real static export at out/ (a ~360 KB index.html + _next/ chunks). The image build (and make webui) run it and overlay webui/dist, so //go:embed bakes the FULL @hanzo/gui console into the host binary. The Dockerfile console stage FAILS HARD if that bundle is missing or degenerate — the placeholder shell can never silently ship to prod (escape hatch: --build-arg ALLOW_PLACEHOLDER=1 for a pure-Go dev image).

Specs

Implements, by the filenames in hanzoai/HIPs:

Status

In production. It serves api.hanzo.ai and the white-label cloud surfaces today, with per-org SQLite (HIP-0302) and the embedded console. manifest/apps.go is the one ordered list of everything mounted — 116 apps, 4 of them eager. For repo-level engineering doctrine (module graph, route-table projections, cross-subsystem seams), see LLM.md.

Hanzo — the Open AI Cloud

Open source · every language · on-chain settlement. hanzo.ai · docs.hanzo.ai

SDKs in every languagePython (flagship) · TypeScript · Go · Rust · C++ · Swift · Kotlin · umbrella

Documentation

Overview

Package cloud is the unified Hanzo Cloud binary per HIP-0106.

One Go binary mounts every Hanzo-native subsystem (iam, base, kms, commerce, ai, gateway, o11y, vfs, mq, dns, amqp, mcp, ...) via the canonical Mount(app cloud.Router, deps cloud.Deps) error contract. Brand, enabled subsystems, and org scope are deployment configuration; the binary is the same artifact across every white-label deployment.

Per HIP-0106 — github.com/hanzoai/HIPs/blob/main/HIPs/hip-0106-unified-hanzo-cloud-binary.md.

Telemetry bootstrap — the ONE site, owned by the HOST.

Every request this process serves gets a span, whether or not o11y happens to be co-resident. That makes the tracer provider a HOST concern: the composition root installs it once, before anything mounts, and owns it for the process life.

It did not used to be. The provider was BUILT inside clients/o11y and handed back through a registered installer — which worked only while clients/o11y was LINKED INTO the host, because its init() did the registering. The moment o11y became a plugin (cloud.PluginSpec -> zip.Load, its own binary) that init() ran in the CHILD, the host's installer stayed nil, and this bootstrap became what its own doc called "a clean no-op": tracing dark fleet-wide, including the gen_ai spans it adopts into the ai module.

What stayed in clients/o11y is what genuinely belongs to o11y: the collector, the datastore exporter, and the SDK-span -> pdata conversion (spanconv.go), which is coupled to the o11y_index_v3 schema and to nothing here. What moved here is the provider, its resource, its lifecycle — and ONE Send.

TRANSPORT — one call site, two deployments:

spans -> traceRouter.Send(traceDest)
          |- Cost 0 : a CO-RESIDENT sink's handler (clients/o11y installs one
          |           via RegisterTraceSink) receives the LIVE span batch.
          '- ErrNoRoute: the ZAP wire (luxfi/trace) to o11y's ZAP receiver.

When o11y is linked in, RegisterTraceSink ran in THIS process and the router takes the Cost-0 leg — no socket, no serialization. When o11y is a plugin, RegisterTraceSink ran in the CHILD, this router has no route, and the SAME Send falls through to the wire — the ZAP hop to the child's receiver. Neither the producer nor the exporter branches on where o11y lives; the routing table answers that, which is the whole point of routing it.

Posture: opt-in and non-fatal. Enabled when a co-resident sink is expected (O11Y_TRACES_ZAP_INPROCESS) OR a ZAP/OTLP wire endpoint is set; a clean no-op otherwise. Nothing dials at install time and the batch span processor exports in the background, so boot never blocks on a collector.

Index

Constants

View Source
const (
	ReasonUnpaid     = "unpaid"
	ReasonUnresolved = "unresolved"
)

The two reasons a 402 can carry, spelled once for every gate. They are DISTINCT codes because the caller's cure is different: an unpaid caller must buy something, an unresolved one must retry.

View Source
const (
	// StageSignup is registration: is this account real, and is it one account?
	StageSignup = "signup"
	// StageUsage is the API/usage plane: is this traffic the customer's, and is
	// it being used the way a customer uses it?
	StageUsage = "usage"
	// StagePayment is authorization time on a transaction.
	StagePayment = "payment"
)

The lifecycle stages a decision can be asked at. A stage selects the feature window and the rule set on the scorer's side; it never selects a different tenant gate.

View Source
const (
	// ActionAllow proceeds.
	ActionAllow = "allow"
	// ActionReview proceeds and summons a person. A statistical judgement may
	// reach here and no further on its own.
	ActionReview = "review"
	// ActionChallenge proceeds only after the caller proves something more.
	ActionChallenge = "challenge"
	// ActionRestrict proceeds at a reduced ceiling.
	ActionRestrict = "restrict"
	// ActionBlock does not proceed.
	ActionBlock = "block"
)

The actions a scorer can return, most permissive first. They are the whole vocabulary: a caller that receives anything else treats it as unrecognised and applies the fail policy, rather than guessing.

View Source
const (
	// RefusalAbsent — no scorer is installed in this process.
	RefusalAbsent = "scorer-absent"
	// RefusalError — the scorer returned an error.
	RefusalError = "scorer-error"
	// RefusalTimeout — the scorer did not answer inside the budget.
	RefusalTimeout = "scorer-timeout"
	// RefusalSilent — the scorer answered with no action.
	RefusalSilent = "scorer-silent"
	// RefusalUnknown — the scorer answered with an action outside the vocabulary.
	RefusalUnknown = "scorer-unknown"
	// RefusalBusy — the scorer was already answering as many questions at once as
	// it is allowed to. The question was not asked.
	RefusalBusy = "scorer-busy"
	// RefusalStuck — the scorer holds every slot and has not returned from ANY
	// call for longer than a stall. It is installed and it is not answering, which
	// is a different fact from busy: busy clears in microseconds, this does not
	// clear at all.
	RefusalStuck = "scorer-stuck"
)

The reasons an answer is not a scored one. A caller that logs, audits or reports an outcome reports this beside it, so "allowed" and "allowed because nobody was listening" are never the same row.

View Source
const AIMeterProvider = "ai"

AIMeterProvider is the commerce provider/service label every inference debit carries, so LLM spend is attributable and the per-scope + account caps sum over (project, "ai"). It is exported so any OTHER inference path that does not run through this wrapper — a BYO model invoked with the org's own provider token (e.g. Cloudflare Workers AI) — debits the SAME product axis and shares the same per-scope caps, rather than inventing a parallel usage label.

View Source
const CountryHeader = "CF-IPCountry"

CountryHeader is the name our edge states the caller's jurisdiction under: the ISO 3166-1 alpha-2 code it resolved from the connecting address. One name, so an operator has one thing to set at the edge and one thing to STRIP from inbound requests.

View Source
const DefaultBrand = brand.Default

DefaultBrand is the fallback brand when CLOUD_BRAND is unknown.

View Source
const DefaultDataDir = datadir.Default

DefaultDataDir is the on-disk data root when CLOUD_DATA_DIR is unset.

View Source
const DefaultModel = "enso-flash"

DefaultModel is the model a Hanzo agent runs on when the caller names none.

It is the FLASH tier on purpose, and pinned rather than left to the router.

The bare `enso` alias does not mean "resolve to the cheapest adequate tier" — its route opens on an Opus-class arm (zen catalog-enso.yaml, route[0]) and bills $4/$20 per Mtok, against enso-flash at $2/$4 and the upstream base at $0.14/$0.28. Defaulting every agent to the bare alias was a 28.6x input / 71.4x output increase on work that mostly wants a fast first response, so the default names the tier it actually wants. A caller who needs more pins enso-pro or enso-ultra; a caller who wants the router's judgement pins `enso`.

This constant is the only literal. The deployment knob (CLOUD_AI_DEFAULT_MODEL, read into Config.AIDefaultModel) defaults to it, every subsystem reads that, and nothing hardcodes a model name of its own. Changing the tier is this line plus the same value in the deployment's env — never a third place.

View Source
const DefaultResourceFeeCents int64 = 100

DefaultResourceFeeCents is the fallback flat provision/create fee (in cents) when no operator override is set — $1.00, so "every service costs money" holds out of the box. It is a configurable POLICY default, not a fabricated market price: ops sets the real number per deployment/kind via ResourceFeeCents's env knobs. Set a kind to 0 to make it free (and therefore un-gated, mirroring the edge gate's price==0 pass-through).

View Source
const HeaderUserBrand = "X-User-Brand"

HeaderUserBrand carries WHICH BRAND'S IAM vouched for the principal, resolved from the token's verified `iss` through the one brand registry (brand.ForIssuer).

It is a SECOND fact, distinct from the deployment's own brand: one cloud binary serves every brand's API host and trusts every brand's issuer (trustedIssuers), so `cfg.Brand` says which brand this PROCESS is and this header says which brand this CALLER is. A plane that keys rows by brand — a tenant key, a derived-key salt — must be able to compare them, because "the process assumed hanzo" and "lux.id signed this" disagreeing is one org's rows landing in another's key space.

It is minted only from validated claims and stripped on ingress like every other authority header, so it is never a value a caller chose. Absent for a principal that carries no issuer to resolve — an hk-/sk- API key, which this deployment's own IAM issued — and a consumer must treat absent as "no second fact to compare", never as a brand.

View Source
const MaxScorerCalls = 256

MaxScorerCalls is how many questions may be in flight at once, process-wide. It is a bound on the COST of asking, not a rate limit: a healthy in-process score returns in microseconds, so this ceiling is never reached by real load — it is reached only when the scorer has stopped answering, which is exactly when asking it again is worthless.

View Source
const PublishablePrefix = "pk-"

PublishablePrefix is the ONE publishable spelling: pk- is the key you may ship in a browser bundle, sk- is the one you may not. Stripe's split, same reason.

View Source
const RiskBudget = 150 * time.Millisecond

RiskBudget bounds how long a decision may take. The gate sits on the request path, so a scorer that hangs must not hang the product: past the budget the answer is the fail policy's, not the scorer's. 150ms is generous for an in-process half-space-tree score and tight enough to be invisible next to any real handler.

View Source
const ScorerStall = 20 * RiskBudget

ScorerStall is how long every slot may be held with nothing coming back before the scorer is called stuck rather than busy. Twenty budgets: far past any burst a healthy scorer produces, and reached in seconds by one that has deadlocked.

View Source
const SpecFile = "openapi.json"

SpecFile is the artifact a describe run writes into the app's own plugin/<app> directory. Named here because the host EMBEDS it (plugin/embed.go) and the weave READS it — one name, so a rename cannot leave a reader looking for a file no writer produces.

View Source
const SwitchPaywallEnforced = "paywall_enforced"

SwitchPaywallEnforced gates the subscription paywall. The key lives here so the registry entry (clients/entitlements), the evaluator (clients/flags) and the edge (serve.go) all name the same string — a switch whose readers disagree about its key is worse than no switch.

View Source
const SwitchPaywallStrict = "paywall_strict"

SwitchPaywallStrict is the posture on an UNRESOLVABLE standing. OFF (the default) = availability: an authority we cannot reach never refuses a customer. ON = revenue: an unresolvable standing refuses. It lives beside its sibling for the same reason — SpendGate (this package) and RequireProduct (clients/ entitlements, which also REGISTERS it) must name one string.

View Source
const TracerName = "hanzo-cloud"

TracerName is the instrumentation scope for cloud's HTTP request spans. It is resolved off the GLOBAL tracer provider — the ZAP provider installed once by the composition root (cloud.Listen initTelemetry, in each plugin) — so a request span ships over the SAME ZAP wire to hanzoai/datastore as every log and GenAI span. One transport, one provider.

View Source
const TrustedProxiesEnv = "CLOUD_TRUSTED_PROXIES"

TrustedProxiesEnv names the operator knob: a comma-separated list of CIDRs and bare addresses that are OUR OWN forwarding hops. Set it when a deployment is fronted by a proxy on a PUBLIC address (a CDN edge, a cloud load balancer with public egress); the default below covers only private space, which is every hop inside a cluster.

Variables

View Source
var (
	// BrandFor returns the brand.Info for a brand id (Hanzo default for unknown).
	BrandFor = brand.For
	// IssuerForBrand returns the canonical OIDC issuer for a brand id.
	IssuerForBrand = brand.IssuerFor
	// BrandForHostOK resolves a request Host to a brand id, ok=false if no brand
	// domain matches.
	BrandForHostOK = brand.ForHostOK
	// BrandForHost is BrandForHostOK with the Hanzo default for an unmatched Host.
	BrandForHost = brand.ForHost
	// BrandIssuers returns the OIDC issuer of every configured white-label brand.
	BrandIssuers = brand.Issuers
)
View Source
var APIKeyPrefixes = []string{"pk-", "sk-"}

APIKeyPrefixes is every opaque-key spelling cloud recognizes at the door. This is the ONE authority. Admission mirrors it rather than importing it (it stays free of cloud-internal imports); if this list changes, that copy must too.

View Source
var ErrGitImporterUnavailable = errors.New("cloud: git importer not registered")

ErrGitImporterUnavailable is returned when the git object plane is not mounted (no importer registered) — a fail-closed sentinel, never a silent success.

View Source
var ErrGitMirrorControllerUnavailable = errors.New("cloud: git mirror controller not registered")

ErrGitMirrorControllerUnavailable is returned when the git object plane is not mounted — fail-closed, never a silent success.

View Source
var ErrIssueSinkUnavailable = errors.New("cloud: tracker issue sink not registered")

ErrIssueSinkUnavailable is returned when the tracker is not mounted — fail-closed, never a silent success, so a feeder logs precisely rather than dropping the item.

View Source
var ErrNoLedger = errors.New("no validated principal")

denial is the ONE decision behind a refused Gate: which status the money wire answers with, and the code+message it carries. Both renderings below read it, so an untyped handler and a typed op can never describe the same refusal two different ways. ErrNoLedger is a priced act with no ledger to charge: there is nobody to bill because there is nobody. It is the ONE value ResourceMeter.Gate answers with when it is handed an empty org, so all of its callers refuse identically without any of them re-deciding it — [denial] renders it as the tenant gate's own 403 rather than as a fault of the biller.

View Source
var ErrNoPeer = plane.ErrNoPeer

ErrNoPeer reports that an app is NOT PART OF THIS DEPLOYMENT. See plane.ErrNoPeer — this is that error, not a second one, so errors.Is holds across both spellings.

View Source
var ErrStoreClosed = errors.New("cloud: org store is closed")

ErrStoreClosed is what every door that opens an org file answers after CloseAll. It is an ERROR and not a silent no-op because a caller that arrives after shutdown is a fact worth surfacing: the request fails, the operator sees why, and nothing resurrects.

View Source
var ErrSyncUnavailable = errors.New("cloud: sync engine not registered")

ErrSyncUnavailable is returned when the engine is not mounted — a fail-closed sentinel, never a silent success, so a trigger logs precisely rather than pretending it synced.

View Source
var Peers func(selector, port string) (org.Source, string, error)

Peers discovers the live sibling pods matching selector, and the namespace watched. An error means "not in a cluster". apps/ installs the Kubernetes implementation; nil means static membership, which always works.

A static peer set makes ha.Owner elect pods that are draining or gone, and the shard router forwards to a dead pod. A ready-gated live set drops a pod the moment it starts terminating.

View Source
var Version = "dev"

Version is the API contract/build version emitted as the X-Api-Version response header — the support-correlation build signal (the brand-neutral analog of the image tag). Stamped at link time by the release build:

-ldflags "-X github.com/hanzoai/cloud.Version=<image tag>"

and overridable at runtime by CLOUD_VERSION, which the operator sets from the deployed image tag without a rebuild (the same channel as CLOUD_BRAND). Defaults to "dev" for an untagged go build / go test.

Functions

func AbuseGate added in v1.801.381

func AbuseGate(deps Deps, t *edge.Traffic) zip.Handler

AbuseGate returns the lifecycle-defense middleware. t is the shared edge sensor; deps carries the policy store, the logger and the metering client.

It is a no-op passthrough when the sensor is absent, mirroring every other gate on this path: an unwired deployment is never blocked.

func Accepted added in v1.801.299

func Accepted(ctx context.Context)

func AccountFromPrincipal added in v1.801.186

func AccountFromPrincipal() zip.Handler

func App added in v1.801.307

func App(name string, cfg *Config, deps Deps, tools zip.Source) *zip.App

App returns an app carrying everything a Hanzo program must carry, in the one order those parts are correct in. It is the only way to obtain one: a program mounts its subsystem on what it gets back and never builds a zip.App itself.

The point is what a caller no longer has the opportunity to forget. Identity is not an option a program passes, it is a property of the value it receives, so a program is either holding an app that identifies its callers or it is holding nothing. Every other member here was equally forgettable and was equally forgotten: the o11y binary assembled its own app and reached production with no panic recovery, no request id, no response-header posture, no tracing, no request log and no typed-op enrichment — a shape nobody chose and nobody could see, because there was nothing to compare it against.

WHERE THE EDGE IS. Production runs ingress → gateway → the front door (cmd/cloud) → this program. The gateway is the public edge and owns rate limiting for the internet. The front door installs no middleware of its own — it routes, serves the console, threads operator flags and scopes credentials — so a program built here is its OWN edge and defends itself. That is why the browser and flood defenses are here rather than borrowed from a parent. A program reached over the plane socket instead trusts what its host asserted: the kernel answers which process is calling, and the boundary's findings travel with the request.

name is what the program calls itself in a diagnostic. tools is the MCP surface, which only a program holding a subsystem list can project — everyone else passes nil and serves none.

func As added in v1.801.350

func As(c *zip.Ctx, org string) context.Context

As delegates THIS request's principal to a call, pointed at a named tenant.

It is for the operator acting on someone else's books: a SuperAdmin migrating org X carries their own identity — which is what the callee re-checks — while the tenant being read is X, not the admin's own org. c.Forward() alone cannot express that, deliberately: it propagates the gateway's assertion unchanged, and the assertion says the admin's org.

So the principal is carried WHOLE and only the tenant is re-pointed, on a context with no request behind it, which is the one place zip reads a stated caller. The authority still comes from the gateway — every other field is the one it minted — and the callee applies its own rules to it. A caller that was not admitted as an admin gains nothing by naming another org, because naming the org was never what granted anything.

org empty keeps the caller's own tenant, so this is also the plain "delegate me" form for a call made from a handler that has no other tenant in mind.

func Ask added in v1.801.350

func Ask[In, Out any](ctx context.Context, app, op string, in *In) (*Out, error)

Ask is the whole client half: dial the app, invoke the op, close.

Prefer the GENERATED client for the peer (plane/<app>) — it is this call with the app name, the op name and the In/Out pair already fixed to each other, so the compiler checks what only a running fleet could check here.

func AuditTrail added in v1.786.1

func AuditTrail(rec *audit.Recorder) zip.Handler

AuditTrail returns the audit middleware bound to rec. A nil rec makes it a no-op passthrough so callers always Use() it unconditionally.

func BYOFeeBps added in v1.801.186

func BYOFeeBps() int64

BYOFeeBps resolves the BYO inference platform fee (basis points) from CLOUD_AI_BYO_FEE_BPS, else the policy default. A negative/invalid value falls through to the default so a typo cannot silently zero out the fee; 0 is honored (free BYO). This is the ONE BYO fee knob for the whole binary.

func BYOFloorMicros added in v1.801.186

func BYOFloorMicros() int64

BYOFloorMicros resolves the per-call BYO floor (micro-USD) from CLOUD_AI_BYO_FLOOR_UUSD, else the policy default. A negative/invalid value falls through to the default.

func BYOInferenceFeeMicros added in v1.801.186

func BYOInferenceFeeMicros(tokens int) int64

BYOInferenceFeeMicros is the per-CALL platform routing fee (micro-USD) for a BYO inference of `tokens` tokens: BYOFeeBps of the EQUIVALENT metered price (aiPriceUUSDPer1kTokens), FLOORED at BYOFloorMicros. A BYO model invoked with the org's own provider token (Cloudflare Workers AI) debits the SAME usage spine and the SAME per-scope caps as a Hanzo-served call, at the thin BYO fee — but never below the floor, so a call the token estimate CANNOT price (a non-text modality: 0 tokens) is still gated and billed rather than slipping through free and unchecked. This is the ONE BYO fee model — no per-provider variant.

func Billable added in v1.801.242

func Billable(method, path string) bool

Billable reports whether a request CONSUMES resource somebody has to pay a provider for, and therefore requires standing before it runs.

THIS IS THE BUG THE SPLIT EXISTS TO KILL. Authorization and pricing were one int64: DefaultPrice returned the edge charge, and BillingGate read `cents <= 0` as "do not gate". Every LLM path prices at 0 there — deliberately, because the ai and zen subsystems meter their own token costs and an edge charge would double-bill — so "we charge nothing HERE" silently meant "we authorize NOTHING here", and the same fusion made every resource kind an operator prices at 0 un-gated as well (ResourceMeter.Gate: `costCents <= 0 -> nil`). One int64 answering two questions is why the LLM leak and the non-LLM gap are a single bug. They are two questions now: Billable says WHETHER standing is required, DefaultPrice says WHAT the edge charges. Pricing something at zero can no longer un-authorize it.

READS ARE NEVER BILLABLE. GET/HEAD/OPTIONS pass unconditionally — gating reads already caused one outage, a balance view that 402s is unusable, and no read this binary serves calls a paid provider.

The path sets are MEASURED, not invented:

  • inference — the exact paths zen's Claim owns (zen@v1.4.2 proxy.go) plus ai's own tree. These are the free-inference hole.
  • meteredTrees — meteredApps resolved through the manifest (the non-LLM auth-not-balance gap). It must agree with the composition root: every surface that declares cloud.Metered spends a provider's money and therefore needs standing, and TestMeteredSurfacesRequireStanding fails if the two drift. The exceptions are named there, not guessed here: commerce is the pay path itself and o11y is telemetry ingest, so both charge nothing and declare cloud.Free.

func BillingGate added in v1.786.1

func BillingGate(m *metering.Client, price func(c *zip.Ctx) int64) zip.Handler

BillingGate returns a zip middleware that gates every request on the caller's commerce balance and records usage for priced paths.

Order of operations:

  1. price(c) == 0 AND not gated → pass straight through (free path).
  2. Authorize(ctx, identity) BEFORE c.Next(): nil → allow. ErrInsufficientBalance → 402 insufficient_balance, c.Next() NOT called. other error → 503 balance_unavailable, c.Next() NOT called (fail-closed; the client returns nil here when built FailOpen, so this branch never fires in fail-open mode).
  3. c.Next() runs the handler chain.
  4. After a successful chain, if price(c) > 0, fire `go m.Record(...)` so the debit never blocks or corrupts the response the user already received.

When m is nil or not configured (no commerce URL) the gate is a no-op: it returns c.Next() directly so an unconfigured deployment is never blocked. price must not be nil; pass DefaultPrice.

func Bridge added in v1.801.299

func Bridge() zip.Handler

Bridge carries into a typed op the request facts its signature drops, and carries back out the one fact it cannot state. Install it BEFORE the typed routes it serves — fiber runs middleware in registration order, so one installed after its leaves never runs — and after the identity boundary, so the identity it parks is the validated one.

It parks the TWO facts a gate turns on, in ONE expression, so they are always set together and can never disagree: the validated ORG (principal.WithOrg, for a plane with rows to scope) and VALIDATED-NESS itself (principal.WithValidated, for a plane whose reads are deployment-global and whose gate is therefore authentication). Both are read back through principal, so a gate that needs either does not reach for the request.

func CallerBearer added in v1.801.89

func CallerBearer(c *zip.Ctx) string

CallerBearer returns the caller's validated JWT bearer for RELAY to a downstream org-scoped service (e.g. the DNS control plane) that authorizes on the caller's OWN identity and derives the org from the token's `owner` claim. It returns the SAME token SanitizeIdentity validated the principal with (callerToken), UNCHANGED -- cloud substitutes NO service credential of its own, so tenant isolation carries across the hop: a caller in org A relays an org-A token and can reach only org A.

An opaque API key (pk-/sk-) is NOT a relayable bearer -- an OIDC target cannot validate it and forwarding it would leak the key -- so it returns "". Empty when the request carries no validatable bearer; the relay then sends no Authorization and the downstream fails closed on its own gate.

func ClientCountry added in v1.801.463

func ClientCountry(c *zip.Ctx) string

ClientCountry is the jurisdiction our edge resolved the caller from, or "" when nothing trustworthy said.

IT IS THE SAME TRUST RULE AS ClientIP, applied to a header instead of a chain, and it is written beside it so the two cannot drift into two rules. A header is a claim; what makes a claim readable is WHO the socket peer is:

a direct caller's header is the CLIENT's own writing. It is refused outright
— reading it would let any caller state its own jurisdiction, which on a rule
that escalates on geography is the same as switching the rule off.

a trusted peer's header is our EDGE's, written after the edge resolved the
address it saw. That is the only version of this fact anybody here holds.

WHAT IT IS NOT. It is the country of the ADDRESS, never of the payer: an address is what a VPN moves and a proxy relays, so this is a weak signal by construction and is documented as one at its one consumer. The strong signal — the billing or KYC jurisdiction of the account — is not derivable in this binary today (there is no billing address, no KYC profile, and the card never touches this process), and inventing one from this would be worse than stating that.

The value is normalised to upper case and refused unless it is exactly two letters. Cloudflare states "XX" for an address it could not place and "T1" for Tor, and neither is a country; both fail the letters test and come back "", which is the same answer as silence and the correct one — no jurisdiction was established.

func ClientIP added in v1.786.1

func ClientIP(c *zip.Ctx) string

ClientIP is the caller's own address: the socket peer for a direct caller, the right-most non-proxy entry of the forwarded chain for a proxied one, and "" for an in-cluster caller that never transited the edge.

It is the ONE client-address read in this repo — the edge rate limiter, the abuse sensor, the audit trail and every metered resource share it, so a forgeable address cannot enter one of them by a side door. It reads EVERY X-Forwarded-For header line, not just the first. fasthttp keeps repeated headers as separate lines, and a client that sends its own line before the proxy appends to a second one would otherwise hide the real address behind a value it chose.

func Created deprecated added in v1.801.299

func Created(ctx context.Context)

Created marks the response 201 Created and Accepted marks it 202 Accepted.

Deprecated: declare the status on the op instead — `zip.WithStatus(201)` — which is the same fact in the one place every projection reads.

These exist because zip once had no vocabulary for a success status other than 200 and 204, so the only way to answer 201 was to set it per request from inside the handler. That works on the wire and nowhere else: the status is a CONTRACT detail, and setting it here writes it into a side channel no projection can read. The document keeps saying 200, so does every SDK generated from it, and the route has always sent 201. It is the same failure as a query parameter's required-ness being invisible — a contract detail that exists only at run time is not a contract.

zip v1.18.2 closed the gap: `zip.Post(app, path, fn, zip.WithStatus(201))` keys the document's response object on 201, so a generated client expects what the service sends. New ops declare it; these two stay so the call sites that predate it keep working while they are converted, and they still set the status they always did.

Both are a no-op off the HTTP path, where there is no status to set.

func DataDir added in v1.801.299

func DataDir() string

DataDir resolves the data root from the environment. It is exported and split out of LoadConfig because credz.Boot must find the same directory BEFORE LoadConfig runs — the credential broker's socket lives there, and the credentials have to be installed before anything reads config or opens a store.

The rule itself lives in internal/datadir because the ROUTER needs it too, and the router cannot link this package. It takes the pod's writer lease on a file under this root before it spawns the children that open stores under it, so the two must resolve the same directory or the lock guards nothing.

func Declare added in v1.801.293

func Declare(specs []Plugin, cfg *Config)

Declare installs the composition root's boot snapshot — the inventory, the prefix table every traced request resolves against, and each surface's declared Price. A disabled subsystem is inventoried but claims NO prefix: it serves nothing, so letting it own a path would attribute another subsystem's requests (or a 404) to it.

MountAll calls it before anything mounts, and that is the only call a running binary makes. It is exported for the tests that must ask what the REAL composition root declares — apps.Wire() — without booting 111 subsystems' stores and dialling their providers to find out. Those tests are why the price a request resolves to can be checked against the number a reviewer approved.

func DefaultPrice added in v1.786.1

func DefaultPrice(c *zip.Ctx) int64

DefaultPrice is cloud's per-request price (in cents) for the edge gate. It holds NO table: it reads the price the surface DECLARED at the composition root (App.Price → PriceOf, see price.go), so the number the gate charges and the number a reviewer approved are the same number, in one place.

It used to be the table, and its last line was `return 0` for anything unlisted — which made a new route free forever, silently, because free never errors. That default is gone: an unpriced surface now fails TestPriceDeclared before it can ship. Undeclared still charges nothing HERE (Price.Cents), because a missing declaration must break the build, never a customer's card.

Two things it does on its own:

  • Health probes are Free regardless of the surface they sit under. A liveness probe that 402s hides whether the process is up, and /v1/<svc>/health is a route of the same surface as everything else under /v1/<svc> — so the moment any surface carries a positive price, its probe has to be exempted here or the exemption has to be written 111 times.
  • A surface declared Metered charges 0, because its meter is downstream: an edge charge on top of it bills the same work twice. That is Price.Cents's job, not a prefix list's.

func Degradations added in v1.801.256

func Degradations() map[string]string

Degradations returns subsystem → reason for every plane that mounted fail-closed. The result is a COPY: a caller must not be able to edit the registry through the map it was handed.

func Degraded added in v1.801.256

func Degraded(subsystem string, err error)

Degraded records that subsystem mounted fail-closed because of err. Calling it twice keeps the FIRST reason: the first failure is the cause, later ones are usually its echo.

func Denied added in v1.801.350

func Denied(err error) error

Denied turns a refused Gate into the error a TYPED op returns. Pair it with DenyEnvelope on the routes that can refuse; without the envelope the refusal still carries the right status and sentence (Unwrap), just in zip's shape.

func DenyEnvelope added in v1.801.350

func DenyEnvelope() zip.Handler

DenyEnvelope writes a Denied refusal back as the money wire's own bytes. Install it on the group whose typed ops gate on balance — and BEFORE those routes, since fiber runs middleware in registration order — so the REST projection answers the 402/503 contract it always has. Anything else passes through untouched.

func DenyResource added in v1.786.1

func DenyResource(c *zip.Ctx, err error) error

DenyResource renders the Gate denial outcomes as the SAME JSON the edge gate returns (denyBilling), so every Hanzo surface emits one error contract: 402 insufficient_balance / 503 balance_unavailable.

It WRITES the response, which is what an untyped handler wants and what a typed op cannot use: zip stamps the op's own status over a hand-written one after the handler returns, so a typed op refuses with Denied instead.

func Describe added in v1.801.350

func Describe(dir string, app *zip.App) error

Describe writes app's projection into dir: the OpenAPI document, from the one live router.

JSON, because JSON is what it IS — the same bytes served at /v1/openapi.json and dropped into hanzoai/openapi. Encoding to YAML would put a yaml library in the graph of every app binary to write a file only the weave reads. Indented so a subset reviews as a diff.

It is rendered whole before the file is touched, so a projection failure leaves the previous one intact rather than truncating it into an app that appears to serve nothing.

func DescribeRequested added in v1.801.350

func DescribeRequested() (string, bool)

DescribeRequested reports whether argv asks this binary to describe itself, and the DIRECTORY it named: `<binary> describe <dir>`. Read before any flag parsing — the mode is a mode, not an option.

A DIRECTORY and not stdout, and the argument is required. A subsystem's own dependencies write to stdout at mount (hanzoai/commerce prints a sqlite-vec warning and GORM debug lines), which a `> file` redirect splices into the front of the document and turns into 71KB of invalid JSON. A writer whose output an unrelated library can corrupt is not a writer.

func Draining added in v1.801.231

func Draining() bool

Draining reports whether a graceful shutdown is in progress.

func EdgeCORS added in v1.786.165

func EdgeCORS(pol *edge.Store) zip.Handler

EdgeCORS returns the browser-CORS middleware for the public /v1 edge. The origin allowlist is the PLATFORM policy's CORSOrigins, read live (recompiled only when it changes) so a SuperAdmin can add/remove origins via PUT /v1/gateway/config.

DEFAULT OFF (empty allowlist ⇒ no-op passthrough). On the RECOMMENDED rollout — the shared Traefik ingress keeps fronting api.hanzo.ai and its `cors-allow-all` middleware already answers CORS there — enabling cloud CORS too would emit a SECOND Access-Control-Allow-Origin header and break every browser preflight. So CORS stays owned by exactly ONE layer: the ingress until/unless the edge moves to a direct DO-LB→cloud path (cloud terminates TLS for api.hanzo.ai), at which point the operator sets CLOUD_CORS_ORIGINS (or PUTs it) and cloud becomes the sole CORS authority. One policy, one place — never both.

When enabled it handles the OPTIONS preflight itself (204, short-circuit) and reflects the allowlisted Origin on the actual response, then continues the chain.

func EdgeRateLimit added in v1.786.165

func EdgeRateLimit(pol *edge.Store) zip.Handler

EdgeRateLimit returns the per-client-IP flood cap that runs BEFORE identity — the gateway's `qos/ratelimit/router` (strategy:"ip") role. The limit + window are read LIVE from the PLATFORM policy (per_ip_rpm / window_sec), so a SuperAdmin can retune the cap via PUT /v1/gateway/config with no redeploy. It is a fixed-window counter keyed on the client IP (leftmost X-Forwarded-For, via ClientIP) with opportunistic eviction of expired windows so the map stays bounded even at the edge's high IP cardinality (why this is not the zip token-bucket primitive that ScopeRateLimit reuses: that primitive never evicts, which is fine for a bounded per-org keyspace but would grow without bound keyed on raw IPs).

SCOPE = public edge only. A request with NO X-Forwarded-For is an IN-CLUSTER direct caller (console BFF, sibling service hitting cloud.svc:8000) — it never transited the ingress/LB, exactly the traffic the standalone gateway never saw, so it is not IP-limited here (parity: the gateway only ever rate-limited public traffic). Only proxied edge traffic, which carries the real client IP in XFF, is throttled. A per_ip_rpm of 0 (env CLOUD_EDGE_RATELIMIT=false at boot) is a live no-op.

func EmbeddedTasks added in v1.786.72

func EmbeddedTasks() *tasksengine.Embedded

EmbeddedTasks returns the ONE in-process tasks engine, or nil until wireDurableIngest has run (or if it failed to start). The Tasks HTTP/UI surface (apps/tasks, mounted at /v1/tasks/*) serves on THIS shared engine — there is exactly one engine per process, shared by ai's durable ingest AND the Tasks product surface, never a second Embed. The surface resolves it lazily (per request) because subsystem Mount runs during MountAll, before wireDurableIngest.

func EmitLifecycle added in v1.800.1

func EmitLifecycle(ctx context.Context, ev LifecycleEvent)

EmitLifecycle fans one event out to every registered subscriber, best-effort and NON-BLOCKING: each subscriber runs in its own goroutine on a cancel-immune context (the fact already happened — a request cancel must not abort the notify/mirror), so a slow reactor (a mirror push) can never delay the git/deploy path or another reactor. A panicking subscriber is contained so one bad reactor can neither crash the shared multi-tenant process nor starve the others; each subscriber logs its own errors.

func EnsureGitMirror added in v1.801.59

func EnsureGitMirror(ctx context.Context, org, project, repo, url string, enabled bool) error

EnsureGitMirror registers (enabled) or removes (disabled) the outbound mirror target url on the native repo. Idempotent.

func ErrorHandler added in v1.801.381

func ErrorHandler(c fiber.Ctx, err error) error

ErrorHandler renders any error a handler propagates. Give it to zip.Config so the app has one, rather than the default that reads only *zip.HTTPError.

It is also the ONE place a fault is recorded. Every 5xx is logged whole — including the ones whose sentence DID survive to the wire, because an operator wants the wrapped chain and the client only ever sees the outermost sentence — with the request id that ties the log line to the response the caller holds.

func EstTokens added in v1.801.186

func EstTokens(texts ...string) int

EstTokens is a deterministic, provider-agnostic token estimate (~4 chars/token) used to price embeddings (no usage returned) and to pre-gate chat before the completion tokens are known.

func Facts added in v1.801.381

func Facts(m map[string]string) map[string]string

Facts drops the empty values from a signal map, because a fact we do not have must be ABSENT rather than empty. An empty string is a VALUE: a scorer keying velocity on "ip" would group every request whose address never arrived — which, behind a load balancer that does not pass the peer, is all of them — into one very busy caller and refuse the lot. "We do not know" and "it is the empty string" are different answers and only one of them is true.

Stated once, at the seam every question passes through, so no gate has to remember it.

func Fail added in v1.801.231

func Fail(c *zip.Ctx, msg string) error

Fail writes a { status:"error", msg } envelope. The operator's transport maps a non-ok envelope to a surfaced error (never a fabricated value).

func Fingerprint added in v1.801.381

func Fingerprint(cred string) string

Fingerprint turns a credential into a stable per-process handle. It is what the sensor counts under and what a traffic report shows — the credential itself never enters a counter, a log, a record or a response.

HMAC-SHA256 rather than a bare hash: with a bare hash, anyone holding a candidate key could confirm it against a published fingerprint. With a keyed digest under a salt that never leaves the process, they cannot.

func For added in v1.801.350

func For(ctx context.Context, org string) context.Context

For states the tenant a BACKGROUND call acts for — a reconcile loop, a grant issued when an org opens, a meter that debits after the response has gone out. Each acts for a tenant with no request to forward, and the callee has to know which one to write to the right books.

An inbound request always wins over this (zip prefers the gateway's assertion), so it supplies an identity where there is none and can never launder one.

func GitRepoStatuses added in v1.801.23

func GitRepoStatuses(ctx context.Context, org, project string, names []string) (map[string]GitRepoStatus, error)

GitRepoStatuses returns the per-repo import + sync status for names (org-scoped).

func Go added in v1.801.261

func Go(log luxlog.Logger, name string, fields []any, fn func())

Go runs fn on a new goroutine with its panic CONTAINED.

This is the one spawn helper for background work in the shared binary, and it exists because of an asymmetry that is easy to miss: middleware.Recover() wraps only the goroutine serving the request. A panic on any goroutine that a handler SPAWNS is unrecovered, and an unrecovered panic does not fail that request — it takes the process down, and with it every tenant and every subsystem in the binary. A malformed page fetched during one org's search would stop everyone's chat.

The surface is large and fed by untrusted input: web pages we fetch and parse, documents we decode, payloads from platforms we bridge. Those are exactly the paths most likely to panic and least likely to be exercised in tests.

name is what appears in the log if fn panics — make it identify the work ("crawl.fetch", "answer.read"), not the call site, because that is what someone reads at 3am. fields are appended verbatim, so pass the org or job id that makes the line actionable; never pass a secret.

It deliberately does NOT return an error, wait, or restart. A caller that needs a result uses a channel; a caller that needs bounding takes a limiter FIRST and releases it in fn (register the release defer before calling Go, so it survives the panic). Containment is this helper's only job.

func Guard added in v1.801.350

func Guard(s Scope, h zip.Handler) zip.Handler

Guard applies the rule to a handler in the standard way: an inadmissible caller is refused 403 before the handler runs, so nothing behind the gate ever observes an unauthorized request.

A subsystem whose REFUSAL has a different shape (the deploy console sends a browser navigation to sign-in) calls Admits directly instead. That is a difference in the answer, not in the rule — the rule is still read from here.

func Handle added in v1.786.216

func Handle[S any](s *Service[S], h func(*Service[S], *zip.Ctx) error) func(*zip.Ctx) error

Handle binds a Service-scoped handler to a route: it adapts a `func(*Service[S], *zip.Ctx) error` to the plain `func(*zip.Ctx) error` the router takes, capturing s. One adapter, so packages write free-function handlers and register them with `app.Get("/path", cloud.Handle(s, myHandler))`.

func IAMBase added in v1.801.408

func IAMBase() string

IAMBase is IAMBaseURL against this deployment's OWN issuer — what a caller with no Config value in hand uses. It exists so "which IAM do I call" has one answer whether or not the caller happens to be holding a Config: three callers resolved it themselves and grew three different fallbacks (the public issuer, nothing at all, and a hardcoded cluster address), which is the disagreement this file was written to prevent and did not.

func IAMBaseURL added in v1.801.293

func IAMBaseURL(publicIssuer string) string

IAMBaseURL resolves the IAM base URL a SERVER-SIDE caller inside the cluster should use — the split-horizon policy, stated once.

The public issuer host (e.g. https://hanzo.id) is fronted by Cloudflare, which 403s a server-side loopback POST with edge error 1006 — so an in-cluster exchange against the public issuer fails and whatever depended on it (the KMS login broker, AI M2M minting, per-tenant identity provisioning) silently stays down (root-caused 2026-07-04: in-cluster POST to https://hanzo.id/... → 403, while http://iam.hanzo.svc/... → 200).

Precedence: the in-cluster IAM service base (IAM_URL — already wired for JWKS), then the public issuer as a last resort (single-process / no split-horizon deploys). Returns "" only when no IAM is resolvable at all. This existed as three inlined copies (ai M2M, the KMS login broker, and the per-tenant identity provisioner would have been the fourth); a copy of a policy does not disagree until one is edited, so there is exactly one now. Endpoint-specific overrides (CLOUD_AI_IAM_TOKEN_URL, CLOUD_KMS_IAM_TOKEN_URL) stay with their endpoints — they override a URL, not this policy.

func IAMExternal added in v1.801.408

func IAMExternal() bool

IAMExternal reports whether this deployment NAMES a separate IAM, as opposed to being the IAM itself (the embedded subsystem).

It is a different question from IAMBaseURL — "is there another IAM" versus "what is its address" — and it is here because answering it separately is how the two disagree: a deployment with only a public issuer resolves a non-empty base yet names no external IAM, and a caller that inferred one from the other reached for a store that is not there.

func IAMIssuer added in v1.801.408

func IAMIssuer() string

IAMIssuer is the PUBLIC identity host this deployment presents (hanzo.id). One name for one fact: the issuer stamped into a token and the issuer a validator checks are the same string, so they are read in one place.

func Identify added in v1.801.437

func Identify(app *zip.App, cfg *Config)

Identify gives an app a trustworthy answer to who is calling, and makes that answer reachable from every route beneath it. App does this for every program, which is the only reason it can no longer be skipped.

The two halves are one function because each is wrong without the other, and wrong in a way nothing reports. IdentityMiddleware deletes the authority headers a client sent and re-mints them from a verified IAM token, so it runs first: what it produces is the only principal in the process anyone may trust. The enrichment then parks that principal on the request context, which is the only path by which a typed op reaches it — a zip.Get[In, Out] handler receives a context and its decoded In and nothing else. Reversed, it parks whatever the caller claimed for itself. Installed alone, the boundary validates a caller and then every typed op reads an empty org and refuses that same caller, which reaches the wire as a 403 from a service behaving exactly as built.

That last failure is the reason this is a function rather than two lines of advice. It is what the o11y binary did while assembling its own app, and its subsystem then compensated from inside its own Mount, on a group node that owned no routes — a program zip refuses to compose, which is the outage.

func IdentityMiddleware added in v1.786.72

func IdentityMiddleware(cfg *Config) zip.Handler

IdentityMiddleware builds the identity trust-boundary middleware from cfg: it constructs the IAM JWT validator (trusted-issuer set, JWKS, audience allowlist) and returns SanitizeIdentity bound to the admin org. This is the ONE constructor for the boundary, so Serve and integration tests wire it identically — no second copy of the validator-construction glue to drift.

func ImportGitRepo added in v1.801.23

func ImportGitRepo(ctx context.Context, req GitImportReq) error

ImportGitRepo creates + mirrors an external repo into the native git server.

func InstallTelemetry added in v1.801.299

func InstallTelemetry(ctx context.Context, log luxlog.Logger, serviceName string) func(context.Context)

InstallTelemetry installs this process's OTel tracer and meter providers and returns a shutdown that flushes and stops both. The returned func is ALWAYS non-nil, so callers defer it unconditionally.

Call it ONCE per process, from the composition root, BEFORE anything mounts. Serve does exactly that — so every per-app plugin entrypoint, which shares its body, installs identically and before ai mounts and reads the adopted-ready flag. It is exported because Serve is not the only composition root: a plugin binary (plugin/o11y) is a host for its own requests and needs the same providers, and one bootstrap that every root calls is the only way that stays true. No root writes its own.

An operator may override serviceName at runtime with OTEL_SERVICE_NAME.

func IsBotActor added in v1.801.293

func IsBotActor(login string) bool

IsBotActor reports whether a push actor is an automation identity rather than a person. Every inbound push transport asks this BEFORE firing OnGitPush: our own release and mirror automation push AS these identities, so without the guard a release's own commit triggers the next release, forever. The two wires we ingest spell a bot differently — GitHub suffixes App-authored logins with "[bot]", our forge attributes workflow-made pushes to its Actions system user — so the ONE predicate knows both.

func IsCommit added in v1.801.455

func IsCommit(s string) bool

IsCommit reports whether s names a git commit: a full 40-char lowercase hex object name, exactly.

It is the ONE rule for that, applied at BOTH ends of the same wire — the builder refuses to pass build-arg:REVISION unless it holds (apps/platform), and this process refuses to report a value unless it holds — so the two cannot drift into disagreeing about what they hand each other. A builder rule laxer than the reader's would pass a branch name that silently became "unknown", which is this outage's failure mode wearing a different hat.

func IsPublishableKey added in v1.801.231

func IsPublishableKey(tok string) bool

func KMSMachineClientID added in v1.801.293

func KMSMachineClientID(org string) string

KMSMachineClientID is the clientId an org's dedicated PaaS-KMS sync application carries — which is also, by the contract above, the audience its tokens are stamped with. Exported for the provisioner (clients/platform), so "<org>-platform-kms" is derived in exactly one place: here, where the recognition side (isKMSMachinePrincipal) reads it back.

func KeyHint added in v1.801.360

func KeyHint(key string) string

KeyHint is the ONE way a key is named in a log line or an error message: its prefix and nothing else. A credential must never be echoed whole, and "sk-902abd…" is enough for a holder to tell WHICH of their keys failed while being useless to anyone who intercepts it.

func Listen added in v1.801.307

func Listen(plugins []Plugin, enable []string) error

Serve boots the canonical compose root and mounts the selected subsystems.

This is the ONE place the cloud-server body lives — every per-app plugin main (plugin/<app>/main.go) calls it, so no boot logic is duplicated per subsystem.

plugins is the composition root's subsystem list (apps.Wire()), threaded in by the caller so cloud never imports subsystems (which would cycle). Listen mounts it in slice order and tears it down in reverse.

enable==nil ⇒ honor cfg.Enable from flags/env (cloud mode; empty = all). enable!=nil ⇒ force exactly that set (single-service mode), overriding --enable so `hanzo kms` is unambiguous.

Listen registers the HIP-0106 liveness contract (GET /v1/<name>/health for every enabled subsystem) before MountAll, runs the canonical middleware pipeline (Recover → RequestID → Logger), and shuts down gracefully on SIGINT/SIGTERM.

func MarkdownNegotiation added in v1.786.165

func MarkdownNegotiation(defaultPrefixes []string) zip.Handler

MarkdownNegotiation returns the response-path middleware. Register it ONCE at the compose root and every /v1 endpoint negotiates markdown uniformly.

func Members added in v1.801.299

func Members(deps Deps) []ha.Member

Members is the writer set this process believes in: the LIVE set when the durable plane elects one, else the static CLOUD_PEERS set the router falls back to. Empty means a single-pod deployment, where this process is the whole fleet.

Exported because the plugin control plane fans out over the membership — and it must be the SAME membership the shard router routes on. Two fleet views that could disagree would be worse than one that is occasionally stale: an operator would roll a version onto a set of hosts that is not the set serving traffic.

func MetricGatherer added in v1.801.381

func MetricGatherer() prometheus.Gatherer

MetricGatherer exposes the registry for the in-process push to the telemetry store (apps/o11y/metricspush.go), which is the ONLY way measurements leave this process now that Prometheus is retired.

The registry is no longer a PUBLISHED SURFACE — there is no exposition and no scraper — it is the buffer the meter provider renders into and the push drains, in the same family model the datastore receiver already speaks. Handing out the Gatherer rather than the *Registry keeps the rule this registry exists to enforce: what appears here is what this process chose to instrument, so a reader cannot quietly become a registrant.

func MicrosToGateCents added in v1.801.186

func MicrosToGateCents(micros int64) int64

MicrosToGateCents converts a micro-USD amount to the whole cents a pre-call balance gate must RESERVE: round UP (a gate must never under-reserve), with a 1-cent floor for any positive amount. 1 cent = 10_000 micro-USD. Shared by the LLM meter and every other inference path (Workers AI) so one rule prices the gate everywhere.

func Mount added in v1.786.216

func Mount[S any](app Router, deps Deps, name string, build func(Base) (S, error), routes func(Router, *Service[S])) error

Mount is the one generic subsystem entrypoint. `build` constructs the typed State from Base (open stores, dial clients — returns an error to fail the mount closed); `routes` registers the handlers. A package's exported Mount is then one line: `return cloud.Mount(app, deps, "name", build, routes)`.

func MountAll

func MountAll(app *zip.App, specs []Plugin, cfg *Config, deps Deps) error

MountAll mounts every ENABLED subsystem in specs, in slice order — the order is the composition root's (apps.Wire()); MountAll does NOT sort.

app is the concrete *zip.App from Serve. A Global spec receives it as its Router. Everyone else receives a scope bound to their declared Prefixes, so a subsystem's middleware reaches its own subtrees and nothing else, whatever its slice position. A subsystem that installs middleware outside them fails the mount — the binary refuses to boot half-gated rather than serving with a stranger's gate on.

Every spec goes through spec.Mount, whatever the grant: the Router it receives is the only difference, which is what makes MountFunc the fleet's ONE mount signature and lets the compiler check it at all 123 composition roots.

Teardown is wired HERE, at mount time: right after a subsystem mounts, its ShutdownFunc (if any) is registered via app.OnShutdown. zip drains those hooks LIFO — AFTER the listeners stop accepting and in-flight requests drain — so registration-at-mount yields reverse-mount teardown (a dependency mounted before its dependents is torn down after them) with no subsystem torn down while a request still uses it. Only ENABLED specs mount, so only they register a hook; teardown needs no separate enablement gate.

func MountMetrics added in v1.801.299

func MountMetrics(app Router, deps Deps) error

MountMetrics adapts hanzoai/metrics into a MountFunc. metrics declares its OWN narrow Deps (Logger, DataDir, Brand) and does not import hanzoai/cloud, so Typed cannot bridge it; this builds that Deps from cloud's and calls metrics.Mount.

It lives here rather than in package apps for exactly CtxShutdown's reason: an apps-local identifier in a Wire entry is unreachable from the generated plugin/<app>/main.go, so metrics fell back to the stub that links every subsystem. The move is affordable only because it is nearly free — cloud's core already carries 398 of metrics' 399 dependencies, so every binary grows by the one package hanzoai/metrics itself. It is the ONLY one of the four mount adapters that is: commerce would add 527 packages to the core, and zen (via hanzoai/ai/controllers) and ai import hanzoai/cloud — a cycle, not a weight.

It is a MountFunc — the doc above always CLAIMED it was one and the signature said otherwise, which is the whole reason a per-entry-point escape hatch is a bad idea. metrics installs no middleware anywhere (hanzoai/metrics calls Use nowhere), so it mounts SCOPED like everyone else; it only ever needed the concrete app to register routes, and cloud.ZipApp is the named hole for that.

func MountPrefixes added in v1.801.264

func MountPrefixes(name string, declared []string) []string

MountPrefixes is the ONE rule for which subtrees a subsystem owns: the prefixes it declared, else the /v1/<name> convention. scope's middleware gate and this index MUST agree on that rule, so they share this function instead of each spelling the fallback — a drift between them would mislabel every span of the subsystem that disagreed.

IT DOES NOT FALL BACK TO manifest.Apps, and the difference is worth stating because the two lists look interchangeable and are not. A manifest row answers "which paths does the HOST forward here" — a routing table, free to enumerate leaves: deploy's row is 14 specific paths (/v1/deploy/applications, /v1/deploy/clusters, …). This answers "which subtree may this subsystem's MIDDLEWARE gate", and deploy installs one bridge across /v1/deploy, the parent of all 14. Feeding the routing table in here made that bridge an escape and failed deploy's boot outright.

So a subsystem whose surface is not /v1/<name> states its own bound — `Prefixes: manifest.PrefixesFor(name)` where the two genuinely coincide, which is what referrals and 21 others do.

func MustOrgNamespace added in v1.801.360

func MustOrgNamespace(org, project string) namespace.Namespace

MustOrgNamespace is OrgNamespace for an org fixed in the source — a test, a seed, a constant in a migration. It panics, which is correct for a value that is wrong before the program runs and wrong for anything from a request.

func OK added in v1.801.231

func OK(c *zip.Ctx, data any) error

OK writes a { status:"ok", data } envelope (the get<T> shape).

func ObserveAlertDelivery added in v1.801.381

func ObserveAlertDelivery(egress, outcome string)

ObserveAlertDelivery records one alert batch's egress outcome. This is the metric behind the meta-alert: paging that cannot reach a human is itself an incident, and the only reason it went unnoticed for months is that nothing counted it.

func ObserveIngest added in v1.801.381

func ObserveIngest(door string, accepted, dropped int)

ObserveIngest records one ingest door's admission outcome.

accepted and dropped are reported TOGETHER because the useful question is a ratio, not a count: "88% of what was offered was dropped" is an outage, "8,000 items were dropped" is a number whose meaning depends on a second series nobody fetched.

func ObserveRows added in v1.801.381

func ObserveRows(table string, n int)

ObserveRows records rows landed in one event STREAM. Called from the two writers that put rows there — the o11y plane sink and the analytics bus drain — so `event.span` counts whichever one is carrying it.

The argument is the stream (event.span, event.log, event.act …), not the physical table: since the occurrence tables merged into event.fact the table no longer identifies what stopped. See warehouseTables.

func OnGitPush added in v1.786.216

func OnGitPush(ctx context.Context, ev GitPushEvent) error

OnGitPush fires the registered push-to-deploy trigger for a landed push. It is a no-op when no builder is registered (git server running without the platform subsystem co-resident). Best-effort by contract: the caller must never fail the push the client already committed just because a build could not be triggered.

func OnServiceRelease added in v1.801.7

func OnServiceRelease(ctx context.Context, ev ServiceReleaseEvent) error

OnServiceRelease rolls a proven image live by patching the matching hanzo.ai/v1 Service CR's spec.image (the operator then reconciles the Deployment). It is a no-op when no releaser is registered (a binary without the paas control plane co-resident). The releaser enforces the clean-semver gate and CR-name resolution; this is only the dispatch seam.

func OrgDB added in v1.786.216

func OrgDB(dataDir string, ns namespace.Namespace, subsystem string) (*sql.DB, error)

OrgDB is the ONE way any cloud subsystem opens a per-entity SQLite file (HIP-0302 physical isolation). It resolves the namespace to its path, creates the parent directory 0700, opens via the sole "sqlite" driver under that namespace's own key, and pins the pool to one connection. The caller owns migration (its schema is its own) and Close.

It takes the NAME rather than the parts a name is made of, so it cannot pair one namespace's path with another namespace's key, and so the question "could this have come from caller input" is asked once — at OrgNamespace, the only door — instead of again at every subsystem that opens a file.

Path convention — see namespace.Key:

org/{slug}            →  {DataDir}/orgs/{slug}/{subsystem}.db
org/{slug}/{project}  →  {DataDir}/orgs/{slug}/projects/{project}/{subsystem}.db
system                →  {DataDir}/orgs/_platform/{subsystem}.db

func OrgForKey added in v1.801.91

func OrgForKey(ctx context.Context, key string) (string, bool)

OrgForKey resolves an opaque Hanzo API key (pk-/sk-) to the org it belongs to — the SAME owner org SanitizeIdentity mints when that key arrives as a bearer — and is the exported door a keyed, bearer-less SDK path uses to attribute a project key to a tenant.

TWO doors in IAM, because a publishable key and a secret key are resolved by different questions and the answers must not be interchangeable:

  • a SECRET key (sk-) asks WHO, and get-user?accessKey answers with the principal. IAM refuses a pk- there BY DESIGN (store.UserByAccessKey), which is right and was also the bug: cloud sent every prefix down this one door, so a publishable key resolved to nothing and the ingest path it exists for could never attribute a beacon. A publishable key that resolves to nobody is a publishable key that does not work.
  • a PUBLISHABLE key (pk-) asks WHICH ORG, and resolve-key answers with the org and nothing else — no user, no email, no admin bit. That is the property that makes it safe to ship in client JS, so it is a separate door with its own narrower capability (CapPublishableResolve), not a flag on the first.

FAILS CLOSED: ("", false) for a non-key-shaped string, an unknown/unresolvable key, an unconfigured resolver, or an out-of-bounds org — never a fabricated or default tenant, so a bad key can never be written into another org's partition. The isAPIKey prefix gate keeps garbage strings off the IAM network path.

func OrgHasUnsafeRune added in v1.786.32

func OrgHasUnsafeRune(s string) bool

OrgHasUnsafeRune reports whether s carries any whitespace, control, or zero-width/format rune — the class that defeats the injectivity of the org→namespace map. strings.TrimSpace (and fasthttp's own header-value OWS trimming) silently drop such runes at the edges, so two DISTINCT IAM org names ("acme" vs "acme ", or an NBSP/ZWSP variant) would collapse onto ONE namespace / image ref — a cross-org fold. The identity trust boundary REFUSES to grant org-scoping from an org bearing one of these (fail secure) instead of folding it, so distinct raw names never collide and no namespace is ever derived from an invisible-character identifier.

Case / '-' / '.' / other visible punctuation are deliberately NOT unsafe: those fold INJECTIVELY through the slugger's hash. Only the invisible / edge-trimmable class — which no injective fold can survive once transport strips it — is rejected. A legitimate IAM org slug never contains such a rune, so no real caller is affected.

It is DERIVED from namespace.Sanitize rather than re-deciding the rune class, because the identity boundary and the slugger have to refuse exactly the same names: this predicate is the reason a request gets no org-scoping, and Sanitize's "" is the reason that org could not have named a database anyway. Two spellings of one rule is a rule that eventually disagrees with itself. The empty org is not "unsafe" — it names nothing, which callers already handle — so it is excluded, exactly as it was when the loop lived here.

func OrgNamespace added in v1.801.360

func OrgNamespace(org, project string) (namespace.Namespace, error)

OrgNamespace names the database an org's records live in — or, when project is non-empty, the database that org's records for one project live in.

org MUST be the VALIDATED principal value (principal.Org, and for the project scope principal.Project), never a raw request body or header. It IS namespace.OrgProject: the org is folded through the ONE injective slugger, so two distinct orgs can never share a namespace, and the project rides in the GROUP slot, which is what a group is for.

It stays spelled here, as cloud's name for that door rather than a second implementation of it, because "which values may name a database" is a question about cloud's principals — and TestOnlyOrgnsBuildsANamespace answers it by proving this file is the only one that asks.

func Peer added in v1.801.307

func Peer(app string) (*zip.Conn, error)

Peer opens a call to another app.

func Plane added in v1.801.350

func Plane() *zip.App

Plane is the app internal ops register on. Every app in this process shares it, because a process serves ONE canonical socket per app name and the ops behind it are that app's whole internal surface.

Declare an op on it exactly as on any app:

zip.Post[plane.SecretIn, plane.Secret](cloud.Plane(), "/kms/get", read,
    zip.WithOperationID(plane.KMSGet))

func Privileged added in v1.801.381

func Privileged(method, path string) bool

Privileged reports whether a request grants standing authority, and therefore whether the scorer's silence must deny it.

It normalizes the path FIRST and compares nothing before it has. That order is the whole fix: the grant lists below describe ROUTES, and a route is what the router says it is.

func Probe added in v1.801.381

func Probe(method, path string) bool

Probe reports whether a request is a liveness/readiness check. A probe is never a grant and is never screened — a health endpoint a risk decision can fail is not a health endpoint, and a kubelet is not a customer.

ONE predicate, read by both the gate's exemption and Privileged below, so the two can never disagree about what a probe is. Read-only methods only: a POST to something ending in "/health" is not a probe, so an attacker cannot name a mutating route into the exemption.

path is normalized here rather than by the caller, so a caller cannot forget.

func Reachable added in v1.801.242

func Reachable(path string) bool

Reachable reports whether a path must be served REGARDLESS of standing.

This is not a scope selector — which routes a gate guards is decided by where it is applied. It is a SAFETY PROPERTY: a customer who has not yet paid must still be able to reach the paths that let them pay, and a lapsed one must be able to cure their own lapse. Gate those and you deadlock every prospect and every lapsed customer at once — a self-inflicted total revenue stop that looks like success in a naive test, because everything returns 402. So the list lives INSIDE the predicate: no future wiring mistake can gate the pay path, whatever it wraps.

Gating too little is a revenue leak we fix next week. Gating too much is an outage. The list is therefore deliberately generous, and anything ambiguous belongs on it.

Traced from the console subscribe flow (console src/components/products/ PlansModule.tsx -> src/lib/api/plans.ts): PlansApi.plans() reads /v1/billing/plans through the per-tenant billing proxy, and checkout drives the /v1/billing/* money surface — which also carries the INBOUND PROVIDER WEBHOOKS at /v1/billing/webhooks/:provider. Gating an inbound payment webhook loses payments outright, so that prefix is load-bearing twice over.

func Refuse added in v1.801.242

func Refuse(c *zip.Ctx, product, reason string) error

Refuse renders the ONE 402 shape every spend gate uses. product is empty for the edge gate (which gates spend itself, not a product) and set by a product-scoped gate; `for <product>` is the only difference it makes to the sentence.

func RegisterCommerceClientFactory added in v1.786.216

func RegisterCommerceClientFactory(f func(cfg *Config, log luxlog.Logger) CommerceClient)

RegisterCommerceClientFactory installs the embedded-Commerce client constructor. apps/commerce.go calls this from its init(); exactly one registration.

func RegisterGitImporter added in v1.801.23

func RegisterGitImporter(g GitImporter)

RegisterGitImporter installs the git object-plane importer. nil-safe.

func RegisterGitMirrorController added in v1.801.59

func RegisterGitMirrorController(c GitMirrorController)

RegisterGitMirrorController installs the git-plane mirror controller. nil-safe.

func RegisterIssueSink added in v1.801.218

func RegisterIssueSink(fn IssueSink)

RegisterIssueSink installs the tracker upsert sink. nil-safe.

func RegisterKMSClientFactory added in v1.786.165

func RegisterKMSClientFactory(f func(cfg *Config, dur *org.Durability, log luxlog.Logger) (KMSClient, error))

RegisterKMSClientFactory installs the embedded-KMS constructor. clients/kms calls this from its init(); it is the ONE inversion point that lets the KMS library and its /v1/kms subsystem share one package with no cloud⇄kms cycle.

func RegisterLifecycleSubscriber added in v1.800.1

func RegisterLifecycleSubscriber(fn func(ctx context.Context, ev LifecycleEvent))

RegisterLifecycleSubscriber adds a git-lifecycle reactor. Every subsystem that reacts to a push/deploy (mirror-out, Slack-notify) registers ONE here at Mount; git/platform EMIT via EmitLifecycle. The inversion keeps the emitters from importing the subscribers — the same pattern as RegisterPushBuilder, but many-registrant.

func RegisterOrgScopeResolver added in v1.786.216

func RegisterOrgScopeResolver(r OrgScopeResolver)

RegisterOrgScopeResolver adds a project-ownership registry consulted by the identity trust boundary. Called once per registry at its Mount (mirrors sites.SetResolver). Registries COMPOSE: a project is "mine" if ANY registry owns it for the org, and "foreign" only if some registry owns it for another org while NONE owns it for this org. A nil resolver is ignored.

func RegisterPushBuilder added in v1.786.216

func RegisterPushBuilder(f func(ctx context.Context, ev GitPushEvent) error)

RegisterPushBuilder installs the git-push-to-deploy trigger. clients/platform calls this from its Mount when co-resident; it is the ONE inversion point that lets the embedded git server launch a platform build with no git⇄platform cycle.

func RegisterServiceReleaser added in v1.801.7

func RegisterServiceReleaser(f func(ctx context.Context, ev ServiceReleaseEvent) error)

RegisterServiceReleaser installs the first-party CR-rollout hook. clients/paas calls this from its Mount when co-resident; it is the ONE inversion point that lets a build-completion path roll a proven image onto its operator Service CR with no cloud⇄paas import cycle.

func RegisterSync added in v1.801.59

func RegisterSync(fn SyncFunc)

RegisterSync installs the sync reconcile func. nil-safe.

func RegisterTraceSink added in v1.801.299

func RegisterTraceSink(sink TraceSink)

RegisterTraceSink installs the co-resident span consumer; a nil sink deregisters it. clients/o11y calls this at mount (and with nil at teardown, so a late export falls back to the wire instead of writing into a closed exporter).

A registration is process-local BY CONSTRUCTION, and that is the mechanism that makes one code path serve both deployments: linked-in o11y registers here and wins the Cost-0 leg; plugin o11y registers in its OWN process, this router stays empty, and the exporter's identical Send falls through to the ZAP wire.

func RepoFromCloneURL added in v1.800.1

func RepoFromCloneURL(u string) string

RepoFromCloneURL extracts the repo name from a git clone URL (last path segment, ".git" stripped) — the repo component of the (org,project,repo) routing key that a deploy emitter derives from an app/project's linked RepoURL. The ONE place this derivation lives, shared by the platform + projects deploy paths (no per-package copy).

func Request added in v1.801.299

func Request(ctx context.Context) (*zip.Ctx, bool)

Request returns the request a typed op is serving, for the ops that must FORWARD the caller's identity rather than merely read it (see the package note). Absent off the HTTP path, where the honest answer is that there is no request — a caller that needs one refuses rather than inventing an identity.

func ResetLifecycleSubscribers added in v1.800.1

func ResetLifecycleSubscribers()

ResetLifecycleSubscribers clears the registry. TEST-ONLY seam (a test mounts and unmounts repeatedly); production registers once at Mount and never resets.

func ResetPlane added in v1.801.350

func ResetPlane()

ResetPlane drops the process's plane app and every op declared on it.

Mount runs again in tests, and zip's op registry APPENDS: a second registration of the same op sits BEHIND the first, so a test would be answered by a previous test's handler — with that test's state, silently, and looking like a pass. The hand-written registry this replaced allowed re-exposing a name because of exactly that, and this is where the property went.

A process mounts once, so nothing in production calls this.

func ResourceFeeCents added in v1.786.1

func ResourceFeeCents(envPrefix, kind string) int64

ResourceFeeCents resolves the flat create fee (in cents) for kind from operator config, most specific first:

<envPrefix>_<KIND>   e.g. CLOUD_PROVISION_FEE_CENTS_SQL=500
<envPrefix>          e.g. CLOUD_PROVISION_FEE_CENTS=100  (all kinds)
DefaultResourceFeeCents                                   ($1.00)

A clearly-named, configurable policy knob — never a fabricated price. A value of 0 makes the kind free (and un-gated). Negative/invalid values are ignored (fall through), so a typo can never make a paid resource free by accident.

func Revision added in v1.801.455

func Revision() string

Revision reports the commit this binary was built from, or "unknown".

UNKNOWN IS A VALUE, NOT A BLANK. Only a full 40-char lowercase hex object name is ever reported; an unexpanded "${REVISION}", a branch name, "dev", a short sha and the empty string all read "unknown". A near-miss is worse than no answer at all, because someone acts on it.

That matters here specifically: `-X` naming a symbol the linker cannot resolve is not an error, it is dropped SILENTLY. A stamp that reached nothing must therefore read as the loud, useless "unknown" rather than as something plausible — and the image build proves the stamp landed by grepping its own linked binaries, because a flag that was merely REQUESTED still appears in `go version -m` output when the symbol was never set.

func RiskScorerInstalled added in v1.801.381

func RiskScorerInstalled() bool

RiskScorerInstalled reports whether a scorer is available — for a health probe or a report, never as a gate. The gate is Decide, which handles absence itself.

func RoutePath added in v1.801.381

func RoutePath(path string) string

RoutePath is a request path in the form THE ROUTER MATCHES IT, and it is the only form a security comparison may use.

fiber resolves a route against its "detection path": the request path lower-cased (CaseSensitive is off) with trailing slashes stripped (StrictRouting is off). c.Path() is the raw spelling the client sent. So `/V1/KMS/secret` and `/v1/kms/` reach exactly the handlers `/v1/kms/...` and `/v1/kms` do, while a prefix test over the raw path matches neither — one capital letter turned a fail-CLOSED grant surface into a fail-OPEN one.

The rule here is the ROUTER'S rule, not an approximation of it: any other normalization would be a second opinion about what a path means, and the router's is the one that decides which handler runs. Percent-encoding is deliberately NOT decoded, for the same reason — fiber does not decode it either (UnescapePath is off), so `/v1/%6bms` routes nowhere and is not a bypass; decoding it here would make this function match a route that does not exist.

func SanitizeIdentity added in v1.786.1

func SanitizeIdentity(v *identityValidator) zip.Handler

SanitizeIdentity returns the identity-trust-boundary middleware.

Per request:

  • ALWAYS delete every header in authorityHeaders (a client copy never survives — this alone kills X-User-IsAdmin forgery).
  • Validate a Bearer / Basic / session-cookie JWT, if present: SuperAdmin (authz.Claims.PlatformSudo — a human MEMBER of the reserved admin org, at any position in the signed `orgs` set). Membership IS the predicate; the isAdmin bit is deliberately not a second term, and IAM does not mint one. This test used to read `homeOrg == adminOrg`, i.e. `Orgs[0].Org` — a POSITIONAL read that IAM's own ordering (home org always first) made true only for a user whose ROW lives in the admin org, so every operator granted admin-org membership was silently refused. → X-User-IsAdmin=true; X-Org-Id = the requested org when present (admin org-switch), else the home org. any other principal (incl. org admins, normal users) → X-Org-Id pinned to owner; NO admin. A client org cannot widen scope.
  • No / invalid / opaque-API-key credential: → NO admin (forgery dead), and the client's X-Org-Id is restored for the Phase-1 data path (residual below).

PHASE-1 RESIDUAL (documented; not a regression vs. today): with no validatable bearer, the client X-Org-Id is passed through for DATA scoping. cloud's data plane has no session of its own yet ("Auth stays gateway-owned in Phase 1") and the console browser data path depends on this header. So a direct-to-pod caller can still SELECT an org for DATA reads. Closing that needs the data path to carry a bearer universally OR the NetworkPolicy locked to gateway-only (which would break console's legitimate direct in-cluster BFF) — Phase-2. The ADMIN boundary (this fix's P0) is closed on every path regardless, because X-User-IsAdmin is NEVER restored from client input.

FAIL MODE. If the validator can't verify a token (JWKS unreachable on a cold cache, issuer/audience misconfigured), the request resolves anonymous and BOTH planes fail SECURE. Admin fails closed (X-User-IsAdmin is never restored from client input), and — since F1 — the DATA plane fails closed too: it gates on a validated principal (clients/principal.Validated) and the anonymous request carries no X-User-Id, so the restored X-Org-Id is refused, not served. Never fails OPEN. The availability cost is bounded to COLD caches: the edge key cache is stale-on-error (a warm cache keeps validating through a transient JWKS outage), so only a from-cold JWKS failure degrades to anonymous-403. The reserved admin org is NOT a parameter. It is the ISSUER's constant — IAM hardcodes it (store.IsSuperAdmin: `owner == "admin"`, and the reserved-org set beneath it) and authz publishes it as authz.AdminOrg — so a consumer-side knob could only ever let cloud DISAGREE with the token contract it is reading. That was not hypothetical: this file's own test doc asserted "Hanzo pins it to 'hanzo'", which, had anyone set IAM_ADMIN_ORG that way, would have handed platform sudo to every member of the hanzo org while IAM considered none of them a SuperAdmin. Production never set it, so the default carried the truth by luck. A knob whose only reachable non-default setting is an estate-wide escalation is not configuration; it is a loaded footgun, and it is now gone.

func ScopeRateLimit added in v1.786.112

func ScopeRateLimit(m *metering.Client, gp *edge.Store) zip.Handler

ScopeRateLimit returns the per-scope rate-limit middleware. It caps an authenticated org from TWO config sources, most-restrictive-wins:

  • commerce spend-alert RateLimitRpm (the plan-configured ceiling), and
  • the /v1/gateway per-org OrgRPM (gp), the runtime-mutable operator override.

It is a no-op passthrough only when BOTH are absent (no commerce AND no policy store), so an unwired deployment is never blocked — mirroring BillingGate.

func ServePlane added in v1.801.350

func ServePlane(name string, log luxlog.Logger) (func() error, error)

ServePlane binds the canonical socket for one app name and serves every op registered on the plane.

Serve calls it for each app this process mounts, AFTER Mount has run, so a resolvable socket always means "the app is up, with its ops live". An app that is up and answers 404 for an op is version skew — a different fact, and separately diagnosable.

It does not return until the socket ACCEPTS. Listening happens in a goroutine, so returning early made "the socket is bound" a race the caller could not see: Serve binds the plane before the app's own listener, and the host waits on that listener to call a child up — so a peer woken through the router could dial a socket that existed as a promise and not yet as a listener. Waiting here is what makes the ordering a guarantee instead of a coincidence.

func ServiceReleaserRegistered added in v1.801.7

func ServiceReleaserRegistered() bool

ServiceReleaserRegistered reports whether a first-party CR-rollout hook is installed (the paas control plane is co-resident). A caller uses it to know whether OnServiceRelease actually patches a CR or is a no-op, so it can be honest about which rollout path took effect.

func SetCompletionCeiling added in v1.801.408

func SetCompletionCeiling(f func(model string) int)

SetCompletionCeiling installs the per-model completion-ceiling lookup the prepaid gate reserves against. Called once at startup by the package that links the model catalog (apps/ai), so package cloud states WHAT it needs without importing where the answer lives — the same seam shape the AI module uses for SetContextWindowResolver.

func SetDraining added in v1.801.231

func SetDraining()

SetDraining marks the process draining (readiness → NotReady). Idempotent.

func SetRiskScorer added in v1.801.381

func SetRiskScorer(fn RiskScorer)

SetRiskScorer installs the ONE scorer. Called by the app that owns /v1/risk when its model and stores are ready. Installing nil uninstalls it, which is how a degraded scorer withdraws rather than answering badly.

func SetRollingCapReader added in v1.801.299

func SetRollingCapReader(f RollingCapReaderFunc)

SetRollingCapReader is the one EXPORTED setter here, and the deviation is forced: the four above are written directly by build.go/durable.go, which are inside this package, but the rolling cap is produced by clients/rollingcap — it imports clients/flags, which imports this package, so it can only ever live above that edge and needs a door. nil clears it (no cap installed).

func SetSwitchReader added in v1.801.218

func SetSwitchReader(f func(string) bool)

SetSwitchReader installs the platform-switch evaluator. clients/flags calls it once on mount; passing nil detaches it (used by tests).

func Severity added in v1.801.463

func Severity(action string) int

Severity ranks the vocabulary above, and it exists so that TWO judgements about one event compose into one answer: the severest of them stands.

It is here rather than at the one gate that fuses today because the ordering IS a property of the vocabulary — the constants are declared "most permissive first" and this makes that sentence executable, in the same file, so the prose and the code cannot drift into two orderings.

AN UNRECOGNISED ACTION RANKS BELOW ALLOW. It is not a milder verdict; it is a string this vocabulary does not contain, and letting one place anywhere in the order would let a typo win a fusion and become the outcome. Ranking it lowest means the judgement that IS recognised decides, and the fail policy — which is Decide's, not this function's — is what answers for the unrecognised one.

func SpendGate added in v1.801.242

func SpendGate(commerce CommerceClient) zip.Handler

SpendGate returns the balance-and-subscription gate serve.go mounts app-wide.

DEFAULT OFF, AND THAT IS A SEQUENCING DECISION, NOT TIMIDITY. A brand-new signup's wallet is $0 and there is NO automatic path that funds it: credit is an admin decision, granted deliberately through the admin surface, and the automatic starter grant that used to run as middleware here has been deleted outright rather than left switched off (an automatic path that mints money is a liability even when disabled, because disabled is one flag away from enabled).

So flipping this gate on 402s every new account from its first request. That is a real product decision — an honest paywall — and NOT one this flag should make silently as a side effect of the grant's removal. Until the paywall's add-credit state is the one a new user actually lands in, enforcing here trades a revenue leak for a signup that dead-ends. The gate therefore stays behind the kill switch, proven by tests, and is flipped as its own deliberate change.

Enforcement is read PER REQUEST from the platform switch an owner flips at admin.hanzo.ai, so it turns on and off within one flag-cache TTL — no redeploy, no CR edit. Switch reports false before the flag engine mounts, so an unmounted switch never enforces.

DECISION ORDER — a request is ADMITTED unless the one proven refusal fires:

  1. enforcement OFF ........................ admit. Read FIRST, so a dark gate costs one atomic load and touches no authority.
  2. not Billable ........................... admit. Reads, the pay path, and every non-metered route are never gated (see Billable / Reachable).
  3. unvalidated principal .................. admit. An anonymous caller is the route's own 401 to make; a 402 is meaningless to someone not signed in, and the org on such a request is a forge anyway.
  4. platform super-admin ................... admit. Platform sudo, including masquerade, is strictly tighter than any purchasable tier and never 402s.
  5. subscribed OR funded ................... admit.
  6. UNRESOLVABLE standing .................. posture (paywall_strict), logged WARN.
  7. proven unpaid .......................... 402 with the actionable refusal.

FAIL POSTURE — argued, not inherited. The refusal in (7) requires PROOF: both authorities answered and both said no. An authority we could not reach yields Unknown, and by default an Unknown ADMITS. That is not "unpaid users get everything free whenever commerce hiccups" — it is "we never call a customer delinquent on evidence we do not have". The asymmetry decides it: refusing on an outage 402s every PAYING customer at once (a total product outage, irreversible), while admitting on an outage leaks access for the duration (bounded, recoverable, and separately capped — the subsystem meters still run their own fail-closed debit). Every fail-open admit logs at WARN with the reason, so a sustained leak pages rather than hides. When an owner wants the other trade, paywall_strict flips it live.

An unresolvable WALLET is an Unknown, not an admit: principal.WalletOf refuses an unresolvable org, and "we could not work out who pays" must refuse AS UNKNOWN and take the strict posture, never be silently waved through as if it were free.

func SubsystemOf added in v1.801.264

func SubsystemOf(path string) string

SubsystemOf resolves the subsystem serving path by longest declared prefix, or "" when nothing owns it (a non-/v1 path, or a route of a disabled subsystem).

func Switch added in v1.801.218

func Switch(key string) bool

Switch reports a platform switch's live value. It returns false before the flag engine mounts and when no engine is present (!cgo), which is the same dark default the engine itself falls back to — so an unmounted switch never enforces.

func Terminal added in v1.801.59

func Terminal(h func(*zip.Ctx) error) func(*zip.Ctx) error

Terminal wraps a handler so a returned *zip.HTTPError is written in-band (its status + the {status,code,error} JSON zip's default errorHandler would emit) and nil is returned, instead of propagating the error up the middleware chain.

It exists for routes mounted UNDER an outer error-flattening filter. The commerce embed installs one: mountCommerce (apps) registers ErrorHandlerJSON on an app.Group("/v1") whose middleware rewrites ANY error a downstream /v1 handler PROPAGATES into a hardcoded HTTP 500 — so a reject that returns zip.ErrUnauthorized (401) or zip.ErrBadRequest (400) up the chain surfaces to the client as 500. A subsystem mounted after commerce (git, sync, integrations, …) whose reject path must keep its real 4xx wraps its handler here: the status is written before the filter runs, so the filter's c.Next() sees nil and has nothing to flatten. A non-HTTPError (a genuine unexpected failure) passes through unchanged — those are 500s regardless. Compose with Handle: cloud.Terminal(cloud.Handle(s, fn)).

func TraceInprocEnabled added in v1.801.299

func TraceInprocEnabled() bool

TraceInprocEnabled reports whether the operator opted the in-process trace sink in. It is the ONE gate, read by the producer here (whether to install a provider at all when no wire endpoint is set) and by clients/o11y (whether to mount the sink) — no second source of truth. Fail-closed: unset ⇒ spans stay on the wire path.

func TracerProviderInstalled added in v1.801.299

func TracerProviderInstalled() bool

TracerProviderInstalled reports whether InstallTelemetry installed the OTel tracer provider in THIS process. It is the one fact the composition root needs in order to adopt the host provider into subsystems that emit their own spans (apps/install.go -> aiobject.AdoptHostTracerProvider). A value, not a callback: the same shape as TierReader/BalanceReader in ai.go, and for the same reason — importing github.com/hanzoai/ai/object here would put 1270 packages under every subsystem that imports cloud for Deps.

func TracingMiddleware added in v1.786.82

func TracingMiddleware() zip.Handler

TracingMiddleware emits ONE OpenTelemetry SERVER span per /v1/* request through the global (ZAP) tracer provider, so every api.hanzo.ai request lands in hanzoai/datastore over the ZAP wire alongside the log pipeline. It records the OTel HTTP semantic-convention attributes (method, route, status), sets the span status on error/5xx, and — critically — PROPAGATES the span context onto the request via SetContext so nested spans (agent.run, agent.step, the LLM chat client span in clients/aihttp.go) parent correctly under the request span. That makes a full agent trace a single tree: request → run → step → chat.

It is a plain zip.Handler wrapping c.Continue(), the framework's idiomatic middleware form (a plain request-scoped handler); no otelfiber shim is needed because zip already exposes method/path/status/context.

func TrustedProxy added in v1.801.381

func TrustedProxy(addr string) bool

TrustedProxy reports whether addr is one of our own forwarding hops — for a health report or a test, never as a gate. The gate is ClientIP, which applies the whole rule.

func UpstreamModel added in v1.801.231

func UpstreamModel(name string) bool

UpstreamModel reports whether name carries an upstream family name.

It matches the family as a WORD, not as a substring: a model id is split on its separators (vendor prefixes, tiers, sizes) and each word has its trailing version digits stripped, so "qwen3.5-397b", "fireworks/deepseek-v3" and "kimi-k2.6" all match while "zen5-coder" and "enso-flash" do not.

func Who added in v1.801.350

func Who(ctx context.Context) zip.Caller

Who reads the principal a plane op is acting for. A handler that needs authority refuses an empty org rather than treating it as permission.

func ZenModel added in v1.801.231

func ZenModel(name string) string

ZenModel returns the Hanzo name for a model: an upstream family name becomes DefaultModel, and a name that is already ours is returned unchanged (trimmed).

Mapping to the default rather than to a guessed tier is the honest answer. We are not claiming which enso tier a given upstream base corresponds to; we are saying the work runs on whatever this deployment runs unnamed work on, which is the same thing we would have told the caller had they named no model.

func ZipApp added in v1.801.299

func ZipApp(r Router) *zip.App

ZipApp recovers the concrete *zip.App behind a Router. It is what zip's TYPED registrars (zip.Get[In, Out] and friends) take, because a typed op is a route PLUS a registry entry — the one value the OpenAPI document, the MCP tool list and the CLI are all projected from — and that registry lives on the App.

This is the same deliberate hole as Fiber(), one level up, and it is safe for the same reason: registering an op is route registration, which scope has never bounded (it bounds middleware). nil means the Router is neither an App nor a scope, which no caller should paper over — a subsystem that cannot reach the registry must fail its mount rather than serve routes no projection knows.

Types

type AIClient

type AIClient = types.AIClient

type Authority added in v1.801.350

type Authority struct {
	Validated bool // a credential was verified — the identity middleware MINTED X-User-Id
	Super     bool // platform sudo: a member of the reserved admin org
	OrgAdmin  bool // admin OF ITS OWN org — org-scoped, self-service
}

Authority is a caller reduced to the three facts an authorization check may read. The two admin bits stay APART — conflating them is a privilege escalation: Super is cross-tenant platform sudo, OrgAdmin administers only its own org and is never platform-privileged.

func AuthorityOf added in v1.801.350

func AuthorityOf(c *zip.Ctx) Authority

AuthorityOf reads one off an HTTP request: the three predicates, and nothing else. It is the ONE extraction — a transport that carries identity some other way (a delegated capability on the internal plane) fills the same value from its own fields and reaches the identical verdict, because the verdict is Scope.Admits and not the extraction.

type BalanceReaderFunc added in v1.801.256

type BalanceReaderFunc func(ctx context.Context, subject, namespace, currency string) (int64, error)

func BalanceReader added in v1.801.256

func BalanceReader() BalanceReaderFunc

type Base added in v1.786.216

type Base struct {
	Log     luxlog.Logger
	KMS     KMSClient
	Bill    *ResourceMeter
	Brand   string
	Env     string
	Domain  string
	DataDir string
	// Durable is the deployment's HA-durability factory (nil ⇒ local-only).
	// NewOrgStore reads it straight off this value, so every per-org file a
	// subsystem opens survives rolling deploys/replicas without the subsystem
	// having to know that it should.
	Durable *org.Durability
}

Base is the shared dependency set every subsystem needs, derived ONCE from Deps at mount. It is embedded in Service, so a handler reaches Log/KMS/Bill/ Brand directly (s.Log, s.Bill, …) with no package re-plumbing them.

func NewBase added in v1.786.216

func NewBase(deps Deps, name string) Base

NewBase derives the shared deps for a named subsystem: a scoped child logger, the embedded KMS client, and the per-org resource meter (provider = name).

Most packages never call this — cloud.Mount does. It is exported for the few subsystems whose Mount is more than build+routes (a background reconciler, a package-global for cross-package hooks, a shutdown cancel): they construct the value directly — `s := &cloud.Service[state]{Base: cloud.NewBase(deps, "x"), State: st}` — then wire routes with Handle, still on the ONE generic type.

func (*Base) Go added in v1.801.261

func (b *Base) Go(name string, fields []any, fn func())

GoBase is Go for a caller that already holds a Base — the common case inside a subsystem, where the logger is s.Base.Log and repeating it at every call site is the kind of noise that makes people skip the helper.

type BaseClient

type BaseClient = types.BaseClient

type ChatRequest

type ChatRequest = types.ChatRequest

type ChatResponse

type ChatResponse = types.ChatResponse

type Claims

type Claims = types.Claims

type CommerceClient

type CommerceClient = types.CommerceClient

type Config

type Config struct {
	// Enable lists subsystems to mount this run. Empty = all enabled.
	// Example: --enable=iam,base,kms,commerce,ai,gateway,o11y
	Enable []string

	// Replicas is the app-tier replica count the operator injects (CLOUD_REPLICAS,
	// mirroring the Deployment's spec.replicas). 0 = unset/unmanaged. It exists to
	// enforce ONE contract: embedded IAM (clients/iam) keeps its identity store as a
	// per-pod embedded SQLite file, so an iam-enabled cloud MUST run at a single
	// replica or each replica gets its OWN divergent identity store. Validate refuses
	// to boot iam-enabled above 1; the helm chart pins replicas=1 whenever "iam" is in
	// --enable. Pointing IAM at a shared external store lifts this.
	Replicas int

	// Brand is the white-label brand identifier.
	Brand string

	// Version is the API contract/build version emitted as the X-Api-Version
	// response header. Sourced from CLOUD_VERSION, else HANZO_VERSION — which the
	// operator sets on every container from the image tag it rendered, so a pod
	// can name its own build without a rebuild — else the link-time cloud.Version
	// default (see version.go).
	Version string

	// Env is the deployment environment (mainnet|testnet|devnet) per the 3-env
	// split. Billing fires in EVERY env — test/dev meter against their own
	// sandbox commerce/Square, never free — so Env is an attribution label, not
	// a gate. Empty when the operator has not set CLOUD_ENV.
	Env string

	// Domain is the deployment's primary public domain.
	Domain string

	// IAMIssuer is the JWKS issuer for JWT validation (usually iam.hanzo.ai).
	IAMIssuer string

	// AdminOrg is the IAM org slug whose members are SuperAdmins (IAM's
	// IsSuperAdmin: owner == AdminOrg). The in-binary identity sanitizer grants
	// admin authority — the c.IsAdmin() that gates /v1/admin/* writes, the
	// /v1/pricing/sync trigger, and the literal "admin" org bucket — ONLY to a
	// validated principal from this org, never to a raw header. Env IAM_ADMIN_ORG
	// (default "admin"), matching the gateway's admin-guard.
	AdminOrg string

	// JWKSURL is the JSON Web Key Set endpoint the identity sanitizer fetches IAM
	// signing keys from. Defaults to {IAMIssuer}/v1/iam/.well-known/jwks
	// (HIP-0111); override with CLOUD_JWKS_URL.
	JWKSURL string

	// KMSMasterKeyRef is the base64-encoded 32-byte KMS master key (KEK) the
	// embedded luxfi/kms store seals every secret's DEK under, and the ONE
	// credential a deployment provisions. The operator injects it from a K8s
	// Secret as CLOUD_KMS_MASTER_KEY_REF; it is never read from the store it
	// hosts (the bootstrap chicken-and-egg) and never logged. Empty ⇒ the KMS
	// subsystem runs fail-closed (health-only).
	//
	// It is sourced from credz, not from the environment: credz.Boot takes it out
	// of the environment at process start so no spawned child inherits it, and
	// holds it in memory. See rootKeyRef.
	KMSMasterKeyRef string

	// KMSMPCAddr / KMSMPCVaultID configure the MPC threshold-signing backend for
	// KMS Sign. Both empty (the default) ⇒ Sign fails closed with a clear error;
	// signing is never fabricated. Set via CLOUD_KMS_MPC_ADDR / CLOUD_KMS_MPC_VAULT_ID.
	KMSMPCAddr    string
	KMSMPCVaultID string

	// DataDir is the on-disk data root.
	DataDir string

	// Role is the HA role of this process (CLOUD_ROLE): the single Writer that
	// owns the RWO stores (default) or a read-only Reader replica. Resolved and
	// validated in Serve; defaults to Writer so an unset variable is byte-identical
	// to today's single-pod deployment.
	Role role.Role

	// WriterURL is the base URL of the single writer (CLOUD_WRITER_URL, e.g.
	// http://cloud-writer.hanzo.svc:8000). A Reader forwards EVERY request here —
	// it opens no stores and is a transparent, always-ready edge that absorbs the
	// writer's rollout gap (see reader_proxy.go). Required when Role==Reader;
	// ignored by a Writer.
	WriterURL string

	// ReaderRetryBudget bounds how long a Reader retries a request the writer
	// could not yet accept (connection refused / no ready endpoint) during a
	// writer roll, before returning 502. Dial-only retry (the request never
	// reached the writer) keeps a non-idempotent POST safe. CLOUD_READER_RETRY_BUDGET
	// (Go duration, default 25s).
	ReaderRetryBudget time.Duration

	// ShardPeers is the CLOUD_PEERS membership list ("id@addr,id2@addr2") of the
	// horizontal-scale StatefulSet: the full, STABLE set of writer pods (each a
	// cloud-N ordinal at its headless per-pod DNS cloud-N.<svc>:8000). Every org is
	// pinned to exactly one owner pod by rendezvous hashing (ha.Owner over this set),
	// so each org's per-org SQLite files are written by ONE pod only; a request whose
	// org this pod does not own is forwarded to the owner (shardrouter.go). Empty or a
	// single entry ⇒ sharding OFF, byte-identical to the single-pod deployment. The
	// set is identical on every pod (static env), so all pods agree on every org's
	// owner — no split-brain dual-writer, no routing loop.
	ShardPeers string

	// ShardSelf is THIS pod's stable id — the StatefulSet ordinal name cloud-N, from
	// CLOUD_POD_NAME (or the downward-API POD_NAME). When sharding is on it MUST be one
	// of ShardPeers' ids (Validate refuses otherwise, so a misconfigured ordinal cannot
	// black-hole every request by forwarding it away with no shard of its own).
	ShardSelf string

	// PeerSelector is the Kubernetes label selector that names this deployment's writer
	// pods (CLOUD_PEER_SELECTOR, e.g. "app.kubernetes.io/name=cloud,app.kubernetes.io/
	// instance=<release>"). When set AND running in-cluster, membership is LIVE — the
	// binary lists Ready, non-terminating pods matching it via the K8s API, so a rolling
	// upgrade's changing pod set is tracked and a draining/dead pod is never elected an
	// org's owner. Empty (dev / native-Go / not in a cluster) falls back to the STATIC
	// ShardPeers/self set — capability-detected, no on/off flag. Deployment wiring the
	// chart sets, not a feature toggle.
	PeerSelector string

	// ListenAddr is the public HTTP listener (default :8080).
	ListenAddr string

	// ZAPListenAddr is the ZAP-RPC listener (default :9653).
	ZAPListenAddr string

	// ZAPWebOrigins is the WebSocket Origin allowlist for the browser-facing
	// /zap ZAP plane (the SPA hosts that may open a ZAP-over-WS connection).
	// Empty == same-origin only. Set via CLOUD_ZAP_WEB_ORIGINS (comma-sep).
	ZAPWebOrigins []string

	// MarkdownDefaultPrefixes lists path prefixes whose successful JSON
	// responses default to markdown (zap-proto/md) when the caller expresses no
	// format preference — the agent-facing endpoints (e.g. /v1/code/, /v1/agents/).
	// A caller always keeps the override: ?format=json or Accept: application/json
	// forces JSON even here, and JSON stays the default everywhere else. Empty ==
	// JSON everywhere unless explicitly negotiated. Env CLOUD_MARKDOWN_DEFAULT_PREFIXES
	// (comma-separated). See middleware_markdown.go.
	MarkdownDefaultPrefixes []string

	// Edge policy (middleware_edge.go) — the "gateway role" cloud absorbs when it
	// serves the public api.hanzo.ai edge directly, no KrakenD hop.
	//
	// CORSOrigins is the browser-CORS allowlist for the /v1 edge. Each entry is an
	// exact origin ("https://hanzo.ai"), a bare host ("hanzo.ai"), or a host
	// wildcard ("*.hanzo.ai" = apex + any subdomain). EMPTY ⇒ CORS is OWNED
	// ELSEWHERE (the shared Traefik ingress `cors-allow-all` fronting api.hanzo.ai)
	// and cloud emits NO CORS headers — set CLOUD_CORS_ORIGINS only on a direct
	// DO-LB→cloud edge, so exactly one layer answers CORS (never both → duplicate
	// ACAO breaks the browser). Read from CLOUD_CORS_ORIGINS — one name, so a
	// deployment cannot half-configure the allowlist under a second spelling.
	CORSOrigins []string

	// EdgeRateEnabled turns on the per-client-IP edge flood cap that runs BEFORE
	// identity (CLOUD_EDGE_RATELIMIT, default true — the gateway enforced this, so
	// dropping it silently would drop a protection). EdgeRatePerIP requests per
	// EdgeRateWindowSec seconds are allowed per public client IP (leftmost
	// X-Forwarded-For); in-cluster direct callers carry no XFF and are exempt.
	// Defaults 100/1s mirror the gateway service-tier client_max_rate (strategy:ip).
	EdgeRateEnabled   bool
	EdgeRatePerIP     int
	EdgeRateWindowSec int

	// HealthListenAddr is the health/metrics listener (default :9090).
	HealthListenAddr string

	// AdminListenAddr is the admin endpoint (default :8081, gated by IAM admin).
	AdminListenAddr string

	// ReadBufferSize is the fasthttp per-conn request-read buffer for the public
	// HTTP edge (zip/fiber), in bytes. fasthttp caps total request-header size at
	// this value and returns 431 (Request Header Fields Too Large) above it. The
	// framework default is 4 KiB — too small once a multi-domain SSO session (an
	// admin-guard Domain=.hanzo.ai cookie set on EVERY subdomain) pushes a
	// browser's request headers past ~4 KiB, 431-ing legitimate requests. This
	// raises the edge ceiling to a sane 32 KiB (nginx large_client_header_buffers
	// parity). Env GATEWAY_READ_BUFFER_SIZE (shared with the gateway edge so both
	// trust boundaries agree on ONE value); tunable down if the per-conn memory
	// budget (SCALE_STANDARD §8) demands it. Internal zip services keep the 4 KiB
	// framework default — only the browser-facing edge opts up.
	ReadBufferSize int

	// BodyLimit is the maximum request body the public HTTP edge (zip/fiber)
	// accepts, in bytes. The framework default is 4 MiB — which silently caps the
	// CONTEXT WINDOW: a chat request carries its whole prompt in the body, and at
	// ~4.3 bytes/token a 1M-token prompt is ~4.3 MB. So the 1M-context models we
	// route to (deepseek-v4-pro; anything glm-5.2 overflows into past its 262,144
	// cap) could not actually be reached — fasthttp refused the body before any
	// handler ran, with the opaque 400 "Error when parsing request" that reads
	// like a malformed payload rather than a size cap. 16 MiB gives 1M tokens
	// real headroom (~3.7x) without inviting a memory-exhaustion vector: the edge
	// is authenticated + rate-limited, and fasthttp streams rather than buffering
	// per-conn. Env GATEWAY_BODY_LIMIT.
	BodyLimit int

	// Endpoints for out-of-process subsystems (payments, vault). Empty
	// means the subsystem is disabled OR the deployment expects a default
	// service-discovery resolution.
	PaymentsZAPAddr string
	VaultZAPAddr    string

	// Billing gate (commerce metering) — the request-edge balance gate.
	//
	// CommerceHTTPURL is the commerce service base over HTTP (the metering
	// client speaks net/http, not ZAP). Empty disables the gate entirely.
	//
	// CommerceServiceToken is the admin-scoped commerce S2S token. It is a
	// SECRET sourced from a KMS-backed secret the operator injects as
	// COMMERCE_SERVICE_TOKEN — never hard-coded or read from disk here.
	//
	// BillingFailOpen flips the gate to allow-on-error. Default is
	// fail-closed (deny when balance can't be determined), matching the
	// gateway. Set only where availability outranks billing.
	CommerceHTTPURL      string
	CommerceServiceToken string
	BillingFailOpen      bool

	// AI inference gateway. Two DISTINCT endpoints, two DISTINCT credentials by
	// concern (see build.go pickCompletionsClient / pickEmbedClient):
	//   - CHAT COMPLETIONS (deps.AI, a WRITE endpoint) — agents/guide/crm/content/
	//     sitegen/code-ask. Authenticated by the IAM M2M identity below, NEVER by a
	//     read-only publishable (pk-) key.
	//   - EMBEDDINGS (deps.Embed, a READ-ONLY endpoint) — code-index + KB. This is
	//     the ONLY consumer of AIAPIKey (the pk- key); read-only is exactly what a
	//     publishable key may do.
	//
	// AIBaseURL is the gateway /v1 root (CLOUD_AI_BASE_URL, default
	// https://api.hanzo.ai/v1); the client appends /chat/completions or /embeddings.
	//
	// AIAPIKey is the KMS-injected static gateway key (CLOUD_AI_API_KEY ←
	// cloud-ai-embed-key). It is a SECRET — never logged, printed, or read from disk
	// here. On the Hanzo deployment it is a read-only PUBLISHABLE (pk-) key: it feeds
	// deps.Embed (read-only, valid) and is REFUSED for deps.AI completions, which the
	// gateway would 403 ("Publishable keys can only access read-only endpoints"). A
	// completions-capable secret key (sk-) set here would instead drive both.
	//
	// AIDefaultModel is the served model an agent with no explicit model falls
	// back to (CLOUD_AI_DEFAULT_MODEL, default DefaultModel — the bare "enso"
	// alias the gateway resolves to a tier per call). Model routing is the
	// gateway's job; this is the ONLY cloud-side model default, and its literal
	// lives in exactly one place (model.go).
	//
	// AIFallbackModel is the reliable model the agent runner fails over to when
	// the agent's own model stays throttled (429/overloaded) after bounded retries
	// (CLOUD_AI_FALLBACK_MODEL, default "best" — the gateway's route-to-best
	// meta-model). It keeps an autonomous bot reply landing when the default flash
	// model is overloaded; the interactive chat path never uses it. Empty disables
	// failover (retry-only).
	AIBaseURL       string
	AIAPIKey        string
	AIDefaultModel  string
	AIFallbackModel string

	// AIAuthClientID / AIAuthClientSecret are the binary's OWN IAM service
	// identity (IAM_CLIENT_ID / IAM_CLIENT_SECRET). The completions client (deps.AI)
	// authenticates to the gateway with a client-credentials (M2M) token minted from
	// this identity and auto-refreshed — the durable no-static-key path, and the ONLY
	// completions credential when AIAPIKey is the read-only pk- embed key. On the
	// Hanzo deployment the identity resolves to admin/hanzo-cloud (gateway-balance-
	// exempt), so cloud's per-org ResourceMeter remains the single debit. The token
	// endpoint is derived from IAMIssuer ({issuer}/v1/iam/oauth/token). The secret is
	// KMS-injected and never logged.
	AIAuthClientID     string
	AIAuthClientSecret string

	// ZAP RPC endpoints for subsystems that are NOT enabled in this
	// process but are still needed by an enabled subsystem. Empty
	// means "no remote endpoint" — the client falls back to the
	// disabled stub which fails closed with a clear error.
	//
	// Convention: <subsystem>.<env>.<deployment>.svc:9653 — the same
	// inter-subsystem listener port the unified binary exposes. The
	// transport is hanzoai/zap, never JSON.
	IAMZAPAddr      string
	BaseZAPAddr     string
	CommerceZAPAddr string
	O11yZAPAddr     string
	VFSZAPAddr      string
	MQZAPAddr       string
}

Config is the cloud binary's startup configuration. Drives which subsystems mount, what brand surface to serve, and where data lives.

func LoadConfig

func LoadConfig() *Config

LoadConfig reads flags + env into a Config. Flags override env.

func SpecConfig added in v1.801.299

func SpecConfig() (*Config, func(), error)

SpecConfig is the deployment the PUBLISHED artifacts describe, and the whole of it — zero values everywhere else, on purpose. The returned func removes the throwaway data dir.

A published spec must be a function of the code alone. Config decides routes: clients/kms registers its secret routes only when a master key resolved, and several subsystems gate on brand. If this read the environment, the artifacts two developers generated from one commit would differ by whichever CLOUD_* variables their shells carried, and the golden would flap in CI for a reason no diff could explain. So it reads nothing.

The data dir is a throwaway and is created HERE rather than taken as an argument, because mounting opens real stores: the default is /var/lib/cloud, and a caller that forgot to override it would either migrate a live store or (as cmd/o11y did) fail on it.

func (*Config) Enabled

func (c *Config) Enabled(name string) bool

Enabled reports whether subsystem `name` is enabled in this config. An empty Enable list mounts everything; a non-empty one mounts exactly what it names. There is no third case.

func (*Config) Validate

func (c *Config) Validate() error

Validate returns an error if the config is missing required values.

type Counter

type Counter = types.Counter

type Cure added in v1.801.242

type Cure struct {
	Kind string `json:"kind"`
	URL  string `json:"url"`
}

Cure is one way out of a 402: Kind names the admit leg it satisfies ("subscribe" | "credit"), URL where to do it.

type DBHandle

type DBHandle = types.DBHandle

type Deps

type Deps struct {
	// Logger is the canonical Hanzo logger (luxfi/log). Subsystems derive
	// scoped child loggers from this.
	Logger luxlog.Logger

	// Brand is the white-label brand identifier for this deployment.
	// Values: "hanzo", "lux", "zoo", "osage", "pars", or any customer brand.
	Brand string

	// Version is the API contract/build version emitted as the X-Api-Version
	// response header (see middleware.ProductionHeaders wiring in serve.go).
	Version string

	// Env is the deployment environment (mainnet|testnet|devnet). Subsystems
	// that meter usage stamp it for per-env attribution; it never gates or
	// bypasses billing (every env bills against its own commerce ledger).
	Env string

	// Self is THIS process's stable id — the StatefulSet ordinal (CLOUD_POD_NAME /
	// POD_NAME) or the OS hostname. It is the SAME id the durability membership
	// elects on (selfID), so a status a subsystem reports names the replica the ring
	// already knows by that name. Any read that is one replica's answer rather than
	// the fleet's must say WHICH replica, or "restarts: 3" is unactionable.
	Self string

	// Domain is the deployment's primary domain (e.g. "api.hanzo.ai",
	// "api.osage.cloud"). Subsystems use this to scope URLs in responses.
	Domain string

	// IAMIssuer is the canonical OIDC issuer (JWKS source) for this brand,
	// resolved from Brand via the white-label registry unless pinned by the
	// operator. Subsystems validate JWT `iss` + signatures against
	// {IAMIssuer}/v1/iam/.well-known/jwks (HIP-0111). One issuer per deployment.
	IAMIssuer string

	// DataDir is the per-deployment data root. Per-org SQLite files
	// land at {DataDir}/orgs/{orgSlug}/{service}.db per HIP-0302.
	DataDir string

	// MasterKey is the 32-byte at-rest KEK (decoded CLOUD_KMS_MASTER_KEY_REF), for
	// subsystems that encrypt their own stores and would otherwise each need a key
	// provisioned separately. One process, one key. nil ⇒ unset/invalid, and each
	// subsystem falls back to whatever it did before.
	MasterKey []byte

	// Durable is the per-deployment HA-durability factory every OrgStore routes
	// through: the shared ha election + vfs FencedStore over the SeaweedFS S3
	// gateway + per-org envelope Cipher. nil ⇒ local-only (no object store creds,
	// dev/single-node), and every OrgStore is exactly the pre-durability cache.
	Durable *org.Durability

	// LiveMembers reads the CURRENT live writer set (the SAME ha.Membership snapshot the
	// durability fencer elects over). Non-nil ONLY when the durable plane is active — the
	// shard router then routes on the live set, so a draining/dead pod's orgs go to the
	// ready successor that hydrates them (M3), not to the gone pod. nil ⇒ the router falls
	// back to the static CLOUD_PEERS set: without the durable plane a peer cannot serve
	// another pod's local-only files, so ownership must stay pinned to the ordinal (which
	// reattaches its PVC across a restart). One field gates the whole live-routing path.
	LiveMembers func() []ha.Member

	// AIDefaultModel is the served model a subsystem uses when a caller supplies
	// none (CLOUD_AI_DEFAULT_MODEL, default DefaultModel = "enso"). It is the ONE
	// cloud-side model default, sourced from config so no subsystem hardcodes a
	// model id. The agents subsystem stores it on an agent created without an
	// explicit model, so a bot launched without a model still runs on a valid
	// catalog model. Model routing itself stays the gateway's job.
	AIDefaultModel string

	// AIFallbackModel is the reliable model the agent runner fails over to when an
	// agent's own model stays throttled after retries (CLOUD_AI_FALLBACK_MODEL,
	// default "best"). Only the autonomous agent/bot run path uses it; interactive
	// chat is untouched. Empty disables failover.
	AIFallbackModel string

	// Subsystem clients — populated by BuildDeps based on enabled subsystems.
	// Each is an interface with both in-process and ZAP-RPC implementations.
	IAM      IAMClient
	KMS      KMSClient
	Base     BaseClient
	Commerce CommerceClient
	// AI runs CHAT COMPLETIONS (a WRITE endpoint): agents, guide, crm, content,
	// sitegen, code /ask. It authenticates with the binary's IAM M2M identity — a
	// completions-capable credential — NEVER the read-only publishable (pk-) embed
	// key, which the gateway 403s on any write endpoint.
	AI AIClient
	// Embed runs EMBEDDINGS (a READ-ONLY endpoint): code-index + KB knowledge. This
	// is the ONLY consumer of the read-only publishable (pk-) key (CLOUD_AI_API_KEY),
	// the correct least-privilege credential for a read-only call. Split from AI so a
	// pk- embed key can never leak onto the completions path (the intermittent-403
	// bug). Falls back to the AI (M2M) resolution when no static embed key is set.
	Embed AIClient
	O11y  O11yClient
	VFS   VFSClient
	MQ    MQClient

	// Payments + Vault stay out-of-process (PCI scope isolation per
	// HIP-0106). These clients always resolve to ZAP-RPC implementations,
	// never in-process.
	Payments PaymentsClient
	Vault    VaultClient

	// Metering is the canonical commerce billing client used by the
	// request-edge BillingGate. It speaks net/http to commerce's billing API
	// (separate from the ZAP Commerce client above, which is for typed
	// inter-subsystem calls). Nil or not-Enabled() makes the gate a no-op.
	Metering *metering.Client

	// Audit is the tamper-evident, append-only audit trail Recorder (FedRAMP AU-*
	// / SOC 2 CC-*). Serve constructs it once, wires the AuditTrail middleware to
	// it, and hands it here so the /v1/admin/audit query + /v1/admin/audit/verify
	// endpoints read the SAME store the middleware writes. Nil makes the audit
	// middleware a no-op and the query endpoint fall back to the IAM proxy (an
	// unconfigured deployment is never blocked). See audit/ and audit_middleware.go.
	Audit *audit.Recorder

	// GatewayPolicy is the runtime-mutable edge-policy store (the /v1/gateway
	// config plane): CORS allowlist + pre-auth per-IP flood cap (platform scope)
	// and the authenticated per-org rate ceiling. BuildDeps constructs it once,
	// layered over the static env/flag defaults; the EdgeCORS/EdgeRateLimit
	// middleware read its PLATFORM policy live and ScopeRateLimit reads its
	// per-org OrgRPM, and the clients/gateway subsystem serves GET/PUT over the
	// SAME store. Never nil — New always returns a working (static-only on store
	// error) *Store, so the edge is never blocked. See clients/edge.
	GatewayPolicy *edge.Store

	// Traffic is the edge's live sensor: per-credential request cadence, path
	// spread, auth-failure rate and the verdict currently held against a caller.
	// BuildDeps constructs it once; AbuseGate (middleware_abuse.go) writes it on
	// every request and the /v1/gateway/traffic op reads the caller's OWN org's
	// slice of it. In-memory and bounded by construction — it is a sensor, not a
	// record, and it is rebuilt from live traffic within one window after a
	// restart. Nil makes the gate a no-op passthrough.
	Traffic *edge.Traffic
}

Deps is the shared dependency surface passed to every subsystem's Mount(app, deps) function. Subsystems consume only what they need.

In-process: each Client below resolves to a direct Go method-call implementation. Out-of-process (legacy split deploys): the same Client resolves to a ZAP-RPC implementation. Subsystem code does not branch on which mode; the interface is the contract.

func BuildDeps

func BuildDeps(cfg *Config) Deps

BuildDeps constructs the Deps used by every subsystem's Mount(app, deps).

Wiring rules per HIP-0106 inter-subsystem contract:

  1. If the subsystem is enabled in this process, the Client field is left nil here. The subsystem's own Mount() will install a typed in-process Client into Deps via the SetClient helpers exposed by this package. (Subsystem Mounts run after BuildDeps; they have full access to construct their concrete implementation, and the resulting object goes back into Deps for everyone else to call.)

  2. If the subsystem is disabled but cfg has a non-empty ZAP RPC endpoint for it, the Client field gets a ZAP-RPC stub targeting that endpoint. Subsystem code calls deps.X.Foo(...) without knowing the call goes over the wire.

  3. If the subsystem is disabled AND there is no endpoint, the Client field gets a "disabled" stub that fails closed with a clear error. Mount-time consumers detect this with clients.IsDisabled(err) and log a friendly "dep X needed by Y not configured" message.

JSON does not appear in any of these paths. Inter-subsystem calls are ZAP-typed Go values either via direct method dispatch (mode 1) or via ZAP RPC over the wire (mode 2). JSON happens only at the gateway/ingress edge, through the zip jsonenc helper.

Payments and Vault are special: they are NEVER in-process per HIP-0106 solo-vault CDE. Their clients always resolve via clients.PaymentsRPCAt / clients.VaultRPCAt; the disabled stub fires when no endpoint is configured.

type EmbedRequest added in v1.786.216

type EmbedRequest = types.EmbedRequest

type GitImportReq added in v1.801.23

type GitImportReq struct {
	Org, Project, Repo string
	CloneURL           string // https://github.com/<owner>/<repo>.git (we construct it)
	Token              string // installation access token; env-only downstream, never argv/logs
	MirrorURL          string // outbound target to register; "" ⇒ don't register
}

GitImportReq imports one external repo into the native git server: create the (Org, Project, Repo) repo if absent, then force-fetch every ref from CloneURL using the short-lived installation Token (mirror-in). Idempotent — a re-import is a re-fetch. When MirrorURL is non-empty an outbound mirror target is registered so a later native push force-safe-mirrors back to the same remote.

type GitImporter added in v1.801.23

type GitImporter interface {
	ImportRepo(ctx context.Context, req GitImportReq) error
	InboundSync(ctx context.Context, req GitInboundReq) (GitSyncResult, error)
	RepoStatus(ctx context.Context, org, project string, names []string) (map[string]GitRepoStatus, error)
}

GitImporter is the git object-plane seam clients/git registers at Mount.

type GitInboundReq added in v1.801.23

type GitInboundReq struct {
	Org, Project, Repo string
	Ref                string // FULL ref, e.g. refs/heads/main or refs/tags/v1.2.3
	CloneURL           string
	Token              string
	Origin             string // source host, e.g. "github.com"
}

GitInboundReq fast-forward-only advances ONE branch from an upstream push (a signature-verified webhook). Native is CANONICAL: the fetch NEVER force- overwrites a native ref — a divergence is reported as a Conflict and native is left unchanged (the split-brain guard). Origin stamps the source host so the outbound mirror suppresses the echo (loop prevention).

type GitMirrorController added in v1.801.59

type GitMirrorController interface {
	EnsureMirror(ctx context.Context, org, project, repo, url string, enabled bool) error
}

GitMirrorController lets the sync engine's git provider ENSURE or REMOVE a native repo's outbound mirror target without importing clients/git (which owns the per-org repo store + the mirror_out reactor that does the actual pushing). enabled=true registers the target (idempotent); enabled=false removes it. The push itself stays with mirror_out on the native push lifecycle — the engine only declares the target, exactly the "cloud ensures the mirror exists; the git plane does the pushing" split.

type GitPushEvent added in v1.786.216

type GitPushEvent struct {
	Org      string
	Project  string
	Repo     string
	Ref      string // FULL ref: refs/heads/<branch> or refs/tags/<tag>
	Commit   string
	CloneURL string
}

GitPushEvent describes a push that just landed on the embedded git server: the org, the repo, the FULL ref that moved (refs/heads/<b> or refs/tags/<t>), and its new tip commit. CloneURL is the canonical clone URL of that repo (https://<host>/v1/git/<org>/<repo>.git) — the exact value an Application's RepoURL carries — so the builder can resolve which app (if any) tracks this ref and needs a rebuild. Tags reach the builder too: releases are cut by tag, so filtering them here would stop publishing silently.

type GitRepoStatus added in v1.801.23

type GitRepoStatus struct {
	Imported     bool  // a native repo exists for this name
	Conflict     bool  // a branch diverged on a prior inbound sync (native preserved)
	LastSyncedAt int64 // unix seconds of the last import/sync (0 = never)
}

GitRepoStatus is the per-repo import + sync status for the console repo list.

type GitSyncResult added in v1.801.23

type GitSyncResult struct {
	Applied  bool   // native advanced (fast-forward); a push.landed was emitted
	NoOp     bool   // already up to date (tip equal — the loop echo) or not imported
	Conflict bool   // native diverged; native was NOT overwritten (split-brain guard)
	Detail   string // human reason (conflict / skip)
	Before   string // native tip before (set on Applied)
	After    string // native tip after (set on Applied)
}

GitSyncResult is the outcome of an inbound fast-forward.

func InboundGitSync added in v1.801.23

func InboundGitSync(ctx context.Context, req GitInboundReq) (GitSyncResult, error)

InboundGitSync fast-forward-only advances one native branch from an upstream push. Never force-overwrites native; a divergence returns Conflict.

type IAMClient

type IAMClient = types.IAMClient

type IngestDialerFunc added in v1.801.256

type IngestDialerFunc func(org string) (tasksclient.Client, error)

func IngestDialer added in v1.801.256

func IngestDialer() IngestDialerFunc

type IntentRequest

type IntentRequest = types.IntentRequest

type IntentResponse

type IntentResponse = types.IntentResponse

type IntentStatus

type IntentStatus = types.IntentStatus

type IssueSink added in v1.801.218

type IssueSink func(ctx context.Context, in IssueUpsert) (IssueUpsertResult, error)

IssueSink is the tracker's upsert entry the sink registers at Mount; feeders reach it via UpsertIssue. A function, not a tracker noun — the one implementation (clients/tracker) registers it, and the feeders never see the store.

type IssueUpsert added in v1.801.218

type IssueUpsert struct {
	Org         string
	Project     string
	ProjectKey  string
	ProjectName string
	Repo        string
	ExtRef      string
	Kind        string
	Source      string
	Title       string
	Description string
	State       string // "open" | "closed"
	Assignee    string
	Labels      []string
}

IssueUpsert is a provider-agnostic external work item mirrored into the native tracker, keyed idempotently by ExtRef so a webhook redelivery or a backfill re-run UPDATES the same row instead of duplicating it. Flat + string-typed so it crosses the feeder→tracker seam without importing the tracker's domain types.

  • Org the tenant (resolved from the signed installation, never a header).
  • Project the IAM project scope; "" ⇒ the org's default project store.
  • ProjectKey the tracker team the item files under (e.g. "GH"); ensured on first use.
  • ProjectName the display name for that team when it is first created (e.g. "GitHub").
  • Repo the git repo the item belongs to — the per-repo filter discriminator.
  • ExtRef the external anchor + idempotency key (e.g. "github:owner/repo#123").
  • Kind/Source what it IS / which surface opened it ("issue"|"pr" / "git").
  • State the upstream open/closed state; the tracker maps it to a board column.
  • Labels upstream label names (the tracker joins them for storage).

type IssueUpsertResult added in v1.801.218

type IssueUpsertResult struct {
	Created    bool
	Number     int
	Identifier string // KEY-<number>
}

IssueUpsertResult reports what the upsert did — Created (a new row) vs updated, plus the tracker identity, so a feeder can log/count precisely (the backfill count).

func UpsertIssue added in v1.801.218

func UpsertIssue(ctx context.Context, in IssueUpsert) (IssueUpsertResult, error)

UpsertIssue mirrors one external work item into the native tracker via the registered sink. Fails closed when the tracker is unmounted.

type KMSClient

type KMSClient = types.KMSClient

type KMSPeer added in v1.801.307

type KMSPeer struct{}

KMSPeer is the KMS client for a process that does not hold the sealed store — which is every process but one.

The tenant comes from the REF, which is already fully qualified ("orgs/<org>/notify/mail/..." is what every caller writes and what the store resolves). It is stated on the call so the two always agree, and the callee refuses a ref outside the org it was called for — that catches an app asking for one tenant while acting for another, which is a bug worth failing on.

It is NOT a trust boundary between our own apps: the socket is 0600 and SO_PEERCRED-authenticated, so a peer is already one of ours and could name any org it liked. Tenancy is enforced where a request principal is resolved. Saying so here keeps the check honest about what it is.

func (KMSPeer) DeleteSecret added in v1.801.350

func (k KMSPeer) DeleteSecret(ctx context.Context, ref string) error

Sign signs a payload with a key that never leaves the store's process — which is the reason signing is an op here rather than a key fetch. DeleteSecret asks the store to forget one secret.

func (KMSPeer) GetSecret added in v1.801.307

func (k KMSPeer) GetSecret(ctx context.Context, ref string) ([]byte, error)

GetSecret reads one secret. A missing secret is an error, never empty bytes: callers treat empty as "not configured" and fail closed on it, so returning empty for a transport failure would read as a deliberate absence.

func (KMSPeer) PutSecret added in v1.801.307

func (k KMSPeer) PutSecret(ctx context.Context, ref string, value []byte) error

PutSecret writes one secret.

func (KMSPeer) Sign added in v1.801.307

func (k KMSPeer) Sign(ctx context.Context, keyRef string, payload []byte) ([]byte, error)

type KeyRefusal added in v1.801.360

type KeyRefusal string

KeyRefusal is the machine-readable reason IAM gives for not resolving a key — `code` on the get-user?accessKey / resolve-key envelope (iam internal/store apikey.go). Cloud does not interpret it; it carries it, so the surface that faces a human can say "revoked, mint a new one" instead of IAM's generic "the entity does not exist". "" means IAM gave no reason (an older IAM, or a store fault, which is NOT a bad credential).

func RefusalForKey added in v1.801.360

func RefusalForKey(ctx context.Context, key string) (KeyRefusal, bool)

RefusalForKey resolves an opaque secret key and reports WHY it failed, for the surface that must explain the failure to a person. It shares resolve()'s cache, so asking why costs no extra IAM call on the hot path: a resolved key answers ("", true) from the same cached principal the auth path uses.

type Licence added in v1.801.242

type Licence uint8

Licence is a subscription authority's ANSWER about one caller, already resolved. The zero value is LicenceUnknown, which is the honest default: a question that could not be asked has no answer, and "no answer" is never "not licensed".

const (
	// LicenceUnknown — the authority could not answer (commerce absent, query
	// failed, nil result). NOT a statement about the caller.
	LicenceUnknown Licence = iota
	// LicenceNone — the authority answered: no live subscription.
	LicenceNone
	// LicenceActive — the authority answered: a live subscription.
	LicenceActive
)

type LicenseEntitlement

type LicenseEntitlement = types.LicenseEntitlement

type LifecycleEvent added in v1.800.1

type LifecycleEvent struct {
	Kind     LifecycleKind
	Org      string
	Project  string
	Repo     string
	Branch   string
	Before   string
	After    string
	Pusher   string
	DeployID string
	Detail   string
	Origin   string
}

LifecycleEvent is one git lifecycle fact fanned out to every registered subscriber. A plain data value — values, not places:

  • Org/Project/Repo the tenant + repo the fact happened in (the routing key).
  • Branch/Before/After the ref that moved and its old→new tip (a push).
  • Pusher who pushed (best-effort; "" for a client-less push).
  • DeployID/Detail the deployment id + a human one-liner (a deploy transition).
  • Origin "" for a native push; the source host when the refs arrived via an inbound mirror sync — the loop-prevention seam that lets the outbound mirror subscriber suppress a re-mirror of refs it just pulled in.

type LifecycleKind added in v1.800.1

type LifecycleKind string

LifecycleKind classifies a git lifecycle event. The value IS the wire name a subscription filters on.

const (
	LifecyclePushLanded   LifecycleKind = "push.landed"
	LifecycleBuildStarted LifecycleKind = "build.started"
	LifecycleDeployLive   LifecycleKind = "deploy.live"
	LifecycleDeployFailed LifecycleKind = "deploy.failed"
)

type MQClient

type MQClient = types.MQClient

type MountFunc

type MountFunc func(app Router, deps Deps) error

MountFunc is a subsystem's mount contract: register your routes on app, using deps for everything shared. Every subsystem in the fleet exports exactly this signature, so Wire references each one directly and the compiler checks it.

app is a Router, not the concrete *zip.App, and that is the whole safety property: middleware a subsystem installs lands on the subtrees its App declares, never over the binary. Routes register exactly as before — absolute paths, same precedence. See scope.go. A subsystem that genuinely gates everything sets Plugin.Global and receives the bare app THROUGH THIS SAME TYPE; the grant changes which Router arrives, never the signature.

THIS IS THE CONTRACT'S ONLY ENFORCEMENT, AND IT IS THE COMPILER. There is no second entry-point field to launder a divergent shape through, so an app whose Mount takes *zip.App, or a differently-aliased Router, cannot be assigned here and cannot reach a composition root. A doc comment stating the signature is what let five apps diverge from it unnoticed; a type is what checks it.

A subsystem that needs the concrete *zip.App (a typed registrar, an embedded module's own mount) recovers it with ZipApp — the named hole, which reports nil rather than pretending, so a mount that truly needs the registry fails instead of serving routes no projection knows.

type O11yClient

type O11yClient = types.O11yClient

type Org

type Org = types.Org

type OrgConfig added in v1.801.59

type OrgConfig = types.OrgConfig

OrgConfig and LicenseEntitlement are the two values CommerceClient's methods name. Both are aliased here for the same reason the interface is: a subsystem that implements CommerceClient (or fakes it in a test) must be able to spell its signature using only this package. LicenseEntitlement was aliased and OrgConfig was not, which made the exported interface unimplementable from outside without reaching into cloud/types — an omission, not a boundary.

type OrgScopeResolver added in v1.786.216

type OrgScopeResolver interface {
	// ProjectOwnership reports, for the project addressed by idOrSlug:
	//   mine  — org itself owns a project with this id/slug,
	//   other — some org OTHER than org owns a project with this id/slug.
	// A store failure is returned as err; the boundary then fails CLOSED (refuses
	// the claim) so a transient registry error can never let a cross-org claim
	// through.
	ProjectOwnership(ctx context.Context, org, idOrSlug string) (mine, other bool, err error)
}

OrgScopeResolver reports the ownership of a project identifier relative to an org, WITHOUT this package importing the registry that holds it. The identifier may be a slug or an opaque id — the implementation matches whichever.

type OrgStore added in v1.786.216

type OrgStore[T io.Closer] struct {
	// contains filtered or unexported fields
}

OrgStore is the lazily-opened, cached set of per-entity stores of type T for one subsystem, each keyed by the NAMESPACE that names it so an entity's SQLite file is opened (and migrated) exactly once. It is the caching layer over OrgDB: every open routes through the same name, path and pragmas, so there is ONE way a subsystem opens its org DBs and ONE hand-rolled map is replaced by this shared value. T is the subsystem's own store handle (it owns its schema via the open func's migration); T must Close its DB.

The key is the namespace and not the path because the namespace is the fact and the path is a rendering of it. Keyed by path, two spellings that render the same file would be two entries and therefore two open handles on one SQLite — which the at-rest cek layer does not support. Keyed by the value, that state cannot be constructed.

func NewOrgStore added in v1.786.216

func NewOrgStore[T io.Closer](b Base, subsystem string, open func(*sql.DB) (T, error)) *OrgStore[T]

NewOrgStore builds a per-org store cache for subsystem, in the deployment b describes. open wraps a freshly-opened *sql.DB (already pragma'd by OrgDB) into the subsystem's store handle, running its migration; it is called once per org file.

It takes the DEPLOYMENT and not three loose parameters because all three — where files live, whether there is an object store to fence against, where a degraded hydrate is reported — are facts of the deployment that Base already holds together. Handed to the store as options, they became a subsystem's opinion: eleven of the fifteen stores simply never passed WithDurable, so their files were local-only on a deployment whose whole point is that they are not. An option that every caller should pass identically is not an option.

func (*OrgStore[T]) CloseAll added in v1.786.216

func (c *OrgStore[T]) CloseAll() error

CloseAll closes every open per-org store. Idempotent; returns the first close error, if any.

func (*OrgStore[T]) Each added in v1.801.186

func (c *OrgStore[T]) Each(fn func(ns namespace.Namespace, st T, err error)) error

Each folds fn over every org that has a {subsystem} store on disk under {dataDir}/orgs, handing it that org's NAMESPACE and the SAME cached store handle For returns (opened through forNS, keyed by the namespace — no second open). It is the cross-org sweep primitive a reconciler folds over: the filesystem is the source of truth for "which orgs have this store", so no derived registry can drift. The reserved platform partitions ({dataDir}/orgs/_*) are skipped (their '_' is a rune namespace.Sanitize never emits, so no real org is dropped). A per-org OPEN failure is passed to fn as its err (fn decides skip vs. record); a missing orgs root (a writer with no stores yet) is not an error. Under horizontal sharding each writer's PVC holds only the orgs routed to it, so Each on a given writer enumerates exactly that writer's orgs.

This is the one thing here that hanzoai/orm/db.Namespaces cannot do and that is not an oversight. That registry's whole surface is With, Open, Close: it resolves a name you already have to a handle, and deliberately knows nothing about which names exist, because knowing would mean holding a directory that can drift from the filesystem. Each answers the opposite question — WHICH entities have a store — and it answers it by reading the disk every time, so there is nothing to drift. A reconciler needs that question answered; until a shared registry offers it without keeping a second copy of the truth, this stays here rather than being pushed upstream as a convenience.

func (*OrgStore[T]) For added in v1.786.216

func (c *OrgStore[T]) For(ns namespace.Namespace) (T, error)

For returns the store the namespace names, opening and migrating it on first use and caching it thereafter. Isolation is PHYSICAL: a distinct namespace resolves to a distinct file, so a query in one can never reach another's rows.

func (*OrgStore[T]) Has added in v1.801.360

func (c *OrgStore[T]) Has(ns namespace.Namespace) bool

Has reports whether the namespace ALREADY has a store for this subsystem on disk, without opening or creating anything.

It exists for reads whose org is named by an UNAUTHENTICATED caller — a public gallery route addressed as /{org}/{project}, say. For calls MkdirAll and opens, so asking it about a name a stranger supplied would let that stranger mint an empty directory and an open handle per name they invent. Has answers the question that route actually has ("is there anything here?") without the side effect, so the caller can 404 a name that names nothing.

An authenticated, org-scoped caller does NOT want this: its org is real by construction and its store must be created on first touch.

func (*OrgStore[T]) Stored added in v1.801.360

func (c *OrgStore[T]) Stored() bool

Stored reports whether ANY namespace already has a store for this subsystem on disk, without opening one.

It is the boot question a reader replica asks — has my volume been hydrated at all? — and it has to be answerable without opening, because a reader that opened every org's file to find out would pay the whole fleet's I/O for one boolean. It is Each's walk without the opens, and it shares Has, so the two cannot disagree about what counts as a store.

func (*OrgStore[T]) Sync added in v1.801.191

func (c *OrgStore[T]) Sync(ns namespace.Namespace) (acked bool, err error)

Sync ships the org's local file to its durable object, fenced at the lease round (the ship-before-ack step a durable subsystem calls after a write commits). It returns acked=false when this replica is not the owner or was deposed mid-request (the caller retries on the new owner). On a local-only store (no Durability) it is a successful no-op — the write is already as durable as configured.

type PaymentsClient

type PaymentsClient = types.PaymentsClient

type PlanChecker added in v1.801.242

type PlanChecker interface {
	ActivePaidPlan(ctx context.Context, org string) (tier string, paid bool, err error)
}

PlanChecker is the ONE commerce read this gate needs: does org X hold a LIVE (active or trialing) PAID plan? It is a consumer-defined interface satisfied structurally by the co-resident commerce client (clients/commerce.ActivePaidPlan) — an OPTIONAL capability resolved from Deps.Commerce by type-assertion, so the narrow types.CommerceClient interface is untouched and a commerce build that cannot answer (split deploy / disabled stub) simply yields LicenceUnknown.

  • (tier, true, nil) -> LicenceActive.
  • ("", false, nil) -> LicenceNone: resolved, no live paid plan.
  • (_, _, err) -> LicenceUnknown: machinery failure, never a "no".

type Plugin added in v1.801.307

type Plugin struct {
	Name     string
	Mount    MountFunc
	Shutdown ShutdownFunc // optional; nil means the subsystem has nothing to tear down.

	// Price is what ONE request to this subsystem's surface costs at the edge gate —
	// Free, Metered, or a positive number of cents (see price.go). It is REQUIRED:
	// the zero value is Undeclared, and TestPriceDeclared fails on it, so a new
	// subsystem cannot reach main until someone writes down what it costs. This is
	// the ONE place a surface's price is declared; DefaultPrice reads it and holds no
	// table of its own.
	Price Price

	// OwnsHealth marks a subsystem that serves its OWN GET /v1/<name>/health
	// (a real, fail-closed probe). Serve's generic liveness loop skips these so
	// its always-ok route never shadows the subsystem's real probe.
	OwnsHealth bool

	// Prefixes are the route subtrees whose middleware this subsystem may install.
	// Empty means the convention it already follows — /v1/<Name>, the same subtree
	// Serve's generic liveness route assumes — so only a subsystem that gates
	// something else has to name it. It bounds MIDDLEWARE, not route registration:
	// routes still register at absolute paths anywhere, as they always have.
	Prefixes []string

	// Global grants app-wide middleware: Mount receives the BARE app as its
	// Router instead of a scope, so what it installs runs for the whole binary.
	// It is the answer for a subsystem that genuinely gates everything (commerce
	// wraps all of /v1) and a lie for anyone else.
	//
	// IT IS A BOOL, AND THAT IS THE POINT. It used to be
	// `App func(*zip.App, Deps) error` — a second Mount FIELD with a second Mount
	// SIGNATURE — which braided two unrelated questions into one declaration:
	// "may this subsystem gate the binary" (policy) and "what shape is its entry
	// point" (type). Because the grant carried its own shape, a subsystem that
	// merely wanted the concrete *zip.App could get it by taking the grant, and
	// three did: agent, ai and commerce each held app-wide middleware authority
	// they had not asked for, purely because their Mount named a concrete type.
	// Nothing reported it — the doc comment claimed apps.TestWireFrozen policed
	// new grants, and no such test exists anywhere in this repo.
	//
	// Unbraided, the shape question has ONE answer for all 140 subsystems —
	// [MountFunc] — so the compiler checks it at every composition root and a
	// sixth divergent signature cannot link. The concrete app is still reachable
	// through [ZipApp], which is the named hole for it and always was; a global
	// subsystem simply gets one whose Router IS the app.
	Global bool

	// Door is this subsystem's PER-CALLER contribution to the MCP door: the tools
	// that exist because of who is asking, which the build-time projection cannot
	// hold. Nil — every subsystem but one — leaves the door exactly the typed ops.
	//
	// It is stated HERE, at the composition root, for the reason Price and
	// Prefixes are: what a binary serves is a property of the binary, declared
	// where the binary is assembled, not installed from inside a Mount that runs
	// after the door is configured.
	Door zip.Source
}

App describes one subsystem to mount. There is NO Order field: the slice position in apps.Wire() IS the mount order — the composition root lists subsystems in the exact sequence they mount (and, reversed, tear down), so order is data read top-to-bottom in one file, not ints scattered across the tree.

type Price added in v1.801.293

type Price int64

Price is what ONE request to a subsystem's surface costs at the edge gate.

A positive value is that many cents, charged per request by BillingGate. The three non-positive values are named, and each says something the number 0 cannot:

Undeclared — nobody has answered. The zero value, and a CI failure.
Free       — costs nothing, decided on purpose.
Metered    — a meter downstream of the edge owns the charge.

It is deliberately NOT a struct: one field on 111 specs has to read as one line.

const (
	// Undeclared is the zero value — the surface's cost is an open question. It is
	// not Free: nobody chose, and TestPriceDeclared fails until somebody does.
	// The edge charges nothing for it (see Cents), so forgetting a declaration can
	// never over-bill a customer; it fails the build instead.
	Undeclared Price = 0

	// Free — this surface costs nothing, on purpose. Health probes, auth, reads, the
	// path to payment itself. Free IS a price: explicit, and reviewed in the diff.
	Free Price = -1

	// Metered — the charge for this surface is owned by a meter DOWNSTREAM of the
	// edge: the subsystem's own ResourceMeter, or the model plane's token meter. The
	// edge must add nothing on top or every request is billed twice. This is the value
	// that says "money moves here, just not here" — spend.go's Billable is what
	// requires standing before it runs.
	Metered Price = -2
)

func PriceOf added in v1.801.293

func PriceOf(path string) Price

PriceOf resolves what the edge charges for path: the declared Price of the subsystem that owns it (price.go). Undeclared when no subsystem owns the path — which includes every request in a process that never ran MountAll, so a caller that reads this as "free" is reading an absence as an answer. Cents() is the one that turns it into money, and it charges nothing for Undeclared.

func (Price) Cents added in v1.801.293

func (p Price) Cents() int64

Cents is what the edge gate charges for one request: the declared cents when the surface is priced at the edge, and zero for every other value. Undeclared charges nothing — a missing declaration must fail the build, never a customer's card.

func (Price) Declared added in v1.801.293

func (p Price) Declared() bool

Declared reports whether somebody answered what this surface costs.

func (Price) MarshalText added in v1.801.293

func (p Price) MarshalText() ([]byte, error)

MarshalText renders the same word JSON readers see, so /v1/admin/subsystems reports "free" / "metered" / "5c" rather than a bare -1 nobody can read.

func (Price) String added in v1.801.293

func (p Price) String() string

String renders the declaration for logs, the admin inventory and test failures.

type Refusal added in v1.801.242

type Refusal struct {
	Error   string `json:"error"`             // stable machine code: always "payment_required"
	Product string `json:"product,omitempty"` // the gated product id, when the gate is product-scoped
	Reason  string `json:"reason"`            // "unpaid" | "unresolved"
	Message string `json:"message"`           // one human sentence
	Cure    []Cure `json:"cure"`              // the ways to fix it, in the order to offer them
}

Refusal is the 402 body. A bare 402 is useless to the console shell, so this names WHAT is gated, WHY, and every way to cure it — one Cure per admit leg, so the body is the structural mirror of the predicate and can never drift from it.

The cure paths are RELATIVE and same-origin, deliberately: a hard-coded cloud.hanzo.ai would brand a Lux / Zoo / Pars deployment as Hanzo, and this binary white-labels by host. They point at the two API surfaces the shell already reads and that Reachable guarantees are never gated — /v1/plans (what to buy, with prices) and /v1/billing (where to pay, subscribe or top up).

type ResourceMeter added in v1.786.1

type ResourceMeter struct {
	// contains filtered or unexported fields
}

ResourceMeter gates and meters per-org spend for non-LLM resource creation, reusing Deps.Metering (the single commerce billing client). Build it with NewResourceMeter. A nil meter, or one whose commerce URL is unset, makes Gate allow and Meter a no-op — so an unconfigured deployment is never blocked, exactly like BillingGate.

func NewResourceMeter added in v1.786.1

func NewResourceMeter(deps Deps, provider string) *ResourceMeter

NewResourceMeter builds a ResourceMeter from the shared deps. provider labels the recorded usage so spend is attributable to the surface that metered it.

func (*ResourceMeter) Enabled added in v1.786.1

func (rm *ResourceMeter) Enabled() bool

Enabled reports whether THIS process holds the ledger — a commerce URL is configured here. False no longer means "nothing bills": once apps are their own binaries the ledger usually lives one socket away, and Gate asks it.

func (*ResourceMeter) Gate added in v1.786.1

func (rm *ResourceMeter) Gate(ctx context.Context, org, project string, projectValidated bool, kind string, costCents int64) error

Gate is the pre-create balance gate. It returns:

nil                              -> allow (balance positive, OR not priced,
                                    OR billing not configured).
metering.ErrInsufficientBalance  -> deny, out of funds (render 402).
other error                      -> balance unknown; fail-closed denies
                                    (render 503). Fail-open returns nil.

costCents<=0 means the kind is free → no gate (mirrors BillingGate's price==0 short-circuit). org MUST be the caller's resolved slug; it is sent as the commerce user AND X-Org-Id so the CALLER's ledger is checked, overriding the client default org — the anti-cross-org property. The balance check honors ctx (a client disconnect/timeout cancels it).

costCents is forwarded as AuthInput.AmountCents so the gate enforces available >= costCents, not merely available > 0 — otherwise a 1-cent balance would authorize an arbitrarily expensive charge (the debit still lands, taking the ledger negative). This mirrors what a prepaid gate must do: refuse a request the balance cannot cover BEFORE the work runs. project + projectValidated are the caller's org SUB-SCOPE and whether it is bound to a VALIDATED identity claim — principal.ValidatedProject(c), the SAME signal the edge BillingGate threads. When validated, a project-scoped spend cap (issue #70) on (project, provider) HARD-enforces (402) on resource creation exactly as on the request edge; when not, it DEGRADES to soft (org- and service-scoped caps stay hard), so a forgeable X-Project-Id can neither hard-stop nor be evaded. service is intrinsically this meter's provider. Pass ("", false) on a background/no-principal path (org- and service-scoped caps only).

func (*ResourceMeter) Meter added in v1.786.1

func (rm *ResourceMeter) Meter(org, project, kind string, amountCents int64, requestID, clientIP string)

Meter records a successful charge to the caller's org ledger. It is the ONE metering entry point for BOTH the one-time create fee AND any recurring footprint charge (storage GB-month, GPU-hour): the caller supplies the amount, so a future recurring meter reuses this same method with a usage-derived amount. No-op when billing is not configured or amountCents<=0.

The debit is fire-and-forget on a background context: the resource already exists, so the charge must never block or corrupt the response the caller received, and a request-context cancellation must not cancel the debit (mirror of BillingGate). A debit failure is logged for reconciliation, not swallowed.

func (*ResourceMeter) MeterUsage added in v1.786.32

func (rm *ResourceMeter) MeterUsage(org, kind string, u metering.Usage)

MeterUsage is the general-purpose per-org debit: it records the caller-built usage event after forcing the per-org billing invariants that make the debit land on the CALLER's ledger and never another org's:

  • u.User and u.Org are OVERWRITTEN to the caller's org slug (the per-org prepaid billing key + the X-Org-Id namespace) — a caller can never bill someone else, and a surface can't accidentally leave them unset (which would debit the client-default org).
  • Provider defaults to the meter's provider; Status defaults to "success"; Currency defaults to "usd".

Everything else the caller supplies (AmountCents, Model, Actor, RequestID, token counts, ClientIP) flows through so a metered surface can attribute spend richly. Like Meter it is fire-and-forget on a background context and a no-op when billing is unconfigured or AmountCents<=0. kind is for the failure log.

type RiskQuery added in v1.801.381

type RiskQuery struct {
	// Stage is the lifecycle moment.
	Stage string `json:"stage"`
	// Subject is the entity being judged.
	Subject RiskSubject `json:"subject"`
	// Agency is the caller's lane as the asking gate classed it — agent, human,
	// bot or unknown. It is a SIGNAL, not an assertion: the scorer may overrule it
	// on facts the gate does not hold, and its answer is the one that counts.
	Agency string `json:"agency,omitempty"`
	// Signals are the facts the gate observed: ip, path spread, failure count,
	// credential class, and whatever else the stage cares about. Free-form because
	// the vocabulary belongs to the scorer's feature inventory, not to the gate.
	Signals map[string]string `json:"signals,omitempty"`
	// Privileged marks a grant of standing authority — a new tenant, a credential,
	// an elevation. It selects the FAIL-CLOSED branch: silence denies. It is set by
	// the gate from the request it is judging, never by the caller being judged.
	Privileged bool `json:"-"`
}

RiskQuery is one question for the scorer.

type RiskScorer added in v1.801.381

type RiskScorer func(ctx context.Context, org string, q RiskQuery) (RiskVerdict, error)

RiskScorer is the scoring function /v1/risk installs. org is the SERVER-resolved tenant; a scorer never re-derives identity, and never answers for a tenant it was not asked about.

type RiskSubject added in v1.801.381

type RiskSubject struct {
	Kind string `json:"kind"`
	ID   string `json:"id"`
}

RiskSubject names WHAT is being judged. Kind is the entity class — account, transaction, session, agent, merchant, payout — and ID is its identity within the tenant. The tenant itself is never in here: it is the org argument, and it comes from the validated principal.

type RiskVerdict added in v1.801.381

type RiskVerdict struct {
	// ID is the scorer's decision id, the handle a decision record is fetched by.
	ID string `json:"id,omitempty"`
	// Action is what to do, from the vocabulary above.
	Action string `json:"action"`
	// Score is the weight of evidence in [0,1].
	Score float64 `json:"score,omitempty"`
	// Agency is the lane the scorer settled on — the authoritative one.
	Agency string `json:"agency,omitempty"`
	// Cause is the scorer's short reason, for the audit record.
	Cause string `json:"cause,omitempty"`
	// Refusal names why this is not a scored answer, and is empty when it is one.
	Refusal string `json:"refusal,omitempty"`
	// Shape is the model SPACE the verdict was reached in, as `<family>:<digest>`.
	// It is what pins an adverse decision to a model — a score is only meaningful
	// against the space that produced it, and "the model that was running" is not
	// an answer to which model decided this. Empty on an unscored answer, which has
	// no space behind it.
	Shape string `json:"shape,omitempty"`
	// Policy is the version of the organisation's decision regime this verdict was
	// reached under. The threshold is derived from the appetite that version
	// states, so it is the record that makes the decision reconstructible after the
	// appetite is restated. Zero means no regime was ever stated and the default
	// posture — shadow — was in force.
	Policy int `json:"policy,omitempty"`
}

RiskVerdict is the answer.

func Decide added in v1.801.381

func Decide(ctx context.Context, org string, q RiskQuery) RiskVerdict

Decide asks the scorer and applies the fail policy. It is the ONLY way cloud code reaches /v1/risk, so the policy is stated once and cannot drift between the gates that depend on it.

It never returns an error. A gate needs an action, and "I could not tell you" IS an action — stated by q.Privileged and carried in Refusal.

func RiskUnavailable added in v1.801.460

func RiskUnavailable(q RiskQuery, why string) RiskVerdict

RiskUnavailable is that same policy, for a scorer that has to state one fact Decide cannot observe from here.

A scorer reached over the plane learns something the seam does not: whether the app it asks is PART OF THIS DEPLOYMENT. Over a socket, "not deployed" arrives as a failed call like any other — and read as an error it would take the fail-CLOSED branch, so a fleet that simply does not run the risk app would refuse every privileged grant in it. That is the outage the absent exemption exists to prevent, and only the caller of the plane can tell it apart from a peer that is here and silent.

So the scorer states the refusal and this applies the ONE policy to it, rather than a second copy of the rule appearing at the one seam that needs it most. Every other refusal stays Decide's to determine.

func (RiskVerdict) Allowed added in v1.801.381

func (v RiskVerdict) Allowed() bool

Allowed reports whether the verdict lets the request proceed unchanged. Review proceeds too — it summons a person, it does not stop traffic.

func (RiskVerdict) Scored added in v1.801.381

func (v RiskVerdict) Scored() bool

Scored reports whether a verdict came from the scorer rather than the fail policy. Every recorder asks this before treating an allow as evidence.

type RollingCapReaderFunc added in v1.801.299

type RollingCapReaderFunc func(ctx context.Context, subject, namespace string) (bool, error)

func RollingCapReader added in v1.801.299

func RollingCapReader() RollingCapReaderFunc

type Router added in v1.801.242

type Router interface {
	// The routing surface is zip.Router — BY REFERENCE, not restated. Ten method
	// lines used to be copied here, and copying an interface makes cloud a second
	// place zip's routing surface is defined: the two agree only for as long as
	// someone keeps them agreeing, and when they disagreed the cost was not a
	// compile error here but a fleet stall. zip v1.23 widened ONE signature (Use
	// took Component instead of Handler) and every implementor that had spelled
	// the methods out — this interface, and the decorators downstream of it — had
	// to be edited in lockstep before v1.19+ could be adopted anywhere.
	//
	// Embedded, a zip routing change costs this file ZERO edits, and the two
	// things below are visibly what cloud ADDS rather than being buried among ten
	// lines cloud merely echoes.
	//
	// It carries zip.OpTarget with it, which is a gain and not a widening: every
	// implementor already had OpScope (scope below, *zip.App, and commerce's
	// mintRouter), and a Router that IS an OpTarget is one a typed registrar —
	// zip.Get[In, Out] — accepts directly.
	zip.Router

	Fiber() *fiber.App

	// Plugins reports every plugin this HOST has loaded — name, prefixes, source,
	// artifact digest, pid, running, uptime, reloads, restarts and kernel-measured
	// usage. It is this replica's answer, never the fleet's: a reader that presents
	// it as fleet-wide is lying about the other pods.
	Plugins() []zip.Status
}

Router is the surface a subsystem mounts on: zip's routing surface EMBEDDED, plus two named, read-only holes onto the host — the *fiber.App the in-process dispatchers need (fiber.Test, GetRoutes, adaptor.FiberApp) and Plugins(), what this process is actually running. *zip.App satisfies it as-is, so Serve can hand the bare app to a Global subsystem, and tests can pass a raw app.

TWO METHODS ARE DECLARED HERE, AND THAT IS THE WHOLE OF WHAT CLOUD ADDS. The end state worth reaching is fewer: `type Router = zip.Router` with Fiber and Plugins as package FUNCTIONS taking a Router — the shape ZipApp below already has, and the shape zip itself chose when it dropped Fiber() from its own Router (a decorator wraps something and has no *fiber.App of its own to return, so requiring one makes decoration impossible). That is what commerce's mintRouter runs into. It is not done here only because the two holes have 131 call sites in apps/, which is a mechanical sweep and a different commit.

Fiber() is a deliberate, named hole: it is the concrete engine, and middleware installed through it is app-wide. It is promoted onto the scoped Router rather than granting those subsystems App — the alternative was four more of them for four read-only uses. Its callers are greppable and none of them registers middleware.

Plugins() is promoted for the same reason and is strictly weaker: it registers nothing and mutates nothing. It exists because the ONE thing config cannot tell you is what a live process actually loaded — deployment manifests answer what was INTENDED, and during a rolling upgrade the two disagree by design. The admin fleet board (/v1/admin/plugins) is the reader; making it Global to ask a read-only question would have granted app-wide middleware to buy a status field.

type Scope added in v1.801.350

type Scope int

Scope is how much authority a route REQUIRES. It is a closed set: there are three kinds of door in the platform, and a fourth would be a new rule, not a new constant.

const (
	// Member admits any validated principal. It is the door on a TENANT surface,
	// where the caller's own org is the whole of what it may reach — the gate
	// establishes that a principal exists, and the handler's own org resolution
	// confines it.
	Member Scope = iota
	// Admin admits a SuperAdmin OR an admin of its own org. It is the door on an
	// administrative READ, where a tenant admin sees its own and platform sudo
	// sees everything — again, the confinement is the handler's.
	Admin
	// Super admits platform sudo alone. It is the door on an act against SHARED
	// platform state, which no customer-org admin may take however much authority
	// they hold inside their own org.
	Super
)

func (Scope) Admits added in v1.801.350

func (s Scope) Admits(a Authority) bool

Admits is the whole authorization rule. Nothing else in the platform decides this question.

type Service added in v1.786.216

type Service[S any] struct {
	Base
	State S
}

Service is THE subsystem value: the shared Base plus the subsystem's own typed State. One generic type across the whole binary; S is plain data (the package's fields). A handler is a free function `func(*Service[S], *zip.Ctx) error` bound with Handle — a package declares no service type, only its State.

type ServiceReleaseEvent added in v1.801.7

type ServiceReleaseEvent struct {
	Service string
	Image   string
	SHA     string
}

ServiceReleaseEvent describes a proven, clean-semver image ready to roll live on an operator-managed first-party service. It is the payload of the release seam that closes push→build→image→CR: after a build produces the image, the CR for this service is patched to it and the operator reconciles the Deployment.

  • Service is the target CR metadata.name (the repo/service name ⇒ CR name, mirroring universe's image-update.yml convention).
  • Image is the full registry ref (repository:tag); the tag MUST be clean semver (vX.Y.Z) — the releaser refuses every mutable/sha/suffixed form.
  • SHA is the source commit for provenance (optional; logged, never gated on).

type ShutdownFunc added in v1.786.32

type ShutdownFunc func(ctx context.Context) error

ShutdownFunc releases a subsystem's process-lifetime resources (background goroutines, open DB handles) on graceful shutdown. It must be idempotent and bounded — Serve calls it within the shutdown deadline. ctx carries that deadline so a slow teardown is cut off rather than hanging SIGTERM.

func CtxShutdown added in v1.801.299

func CtxShutdown(f func() error) ShutdownFunc

CtxShutdown adapts a subsystem's zero-arg Shutdown() error to ShutdownFunc. Several subsystems expose the simpler form (their teardown ignores the deadline); this bridges the impedance mismatch in ONE place so the Wire entries stay declarative — no inline closures.

It lives beside ShutdownFunc rather than in package apps because a Wire entry is copied verbatim into plugin/<app>/main.go by plugin/gen-app-cmds: an apps-local helper is unreachable from a standalone main, so that app falls back to the fat stub that links EVERY subsystem. Package-qualified here, it is nameable from anywhere and the per-app binary stays lean.

type Span

type Span = types.Span

type Standing added in v1.801.242

type Standing uint8

Standing is a caller's resolved commercial standing. The zero value is Unknown, which is the honest default: a question not yet asked has no answer, and "no answer" is never "has not paid".

const (
	// Unknown — at least one authority could not answer (commerce unreachable,
	// ledger unreadable, org unresolvable). NOT a statement about the caller.
	Unknown Standing = iota
	// Unpaid — BOTH authorities answered and both said no: no subscription and no
	// credit. The only proven refusal.
	Unpaid
	// Subscribed — a live subscription admits the caller.
	Subscribed
	// Funded — the wallet holds a positive prepaid balance to burn down.
	Funded
)

func Stand added in v1.801.242

func Stand(ctx context.Context, lic Licence, w principal.Wallet) Standing

Stand composes the two independent legs into the one answer.

lic — the subscription authority's already-resolved answer.
w   — the money address the credit leg reads (principal.WalletOf).

The legs are evaluated cheapest-decisive-first: a licensed caller never touches the ledger. A caller with no subscription always does — that is the pay-as-you-go path, and it is the common case for a prepaid customer.

func (Standing) Admits added in v1.801.242

func (s Standing) Admits() bool

Admits reports whether this standing lets a request through on its own merits. Unknown does NOT admit here — it is not an admit, it is an absence, and the posture that resolves it is enforcement's, deliberately not hidden inside this predicate.

func (Standing) String added in v1.801.242

func (s Standing) String() string

String renders the standing for logs.

type Subsystem added in v1.801.264

type Subsystem struct {
	Name     string   `json:"name"`
	Prefixes []string `json:"prefixes"`
	Enabled  bool     `json:"enabled"`
	// Price is the surface's declared per-request cost at the edge (price.go) —
	// "free", "metered" or "5c". Reported because "what does this cost" is a
	// question about a running deployment, and until it was declared here nobody
	// could answer it for a surface without reading the gate's source.
	Price Price `json:"price"`
}

Subsystem is one entry of the composition root: its name, the route subtrees it owns, and whether this deployment has it switched on. A DISABLED subsystem is still listed — "it is off" is the answer to "why is that board empty", and dropping it from the inventory destroys that answer.

func Subsystems added in v1.801.264

func Subsystems() []Subsystem

Subsystems returns the boot inventory, name-sorted — every configured subsystem and whether it is on. The slice is a fresh copy of an immutable snapshot, so a caller cannot disturb the index every request reads.

type SyncEvent added in v1.801.59

type SyncEvent struct {
	Kind     string // sync kind, e.g. "git"
	Provider string // endpoint the event came from: "github" | "gitlab" | "hanzo-git"
	Org      string // tenant (from the signed installation / gateway identity)
	Locator  string // source repo locator (a clone URL, or "<org>/<repo>")
	Repo     string // short repo name (git)
	Ref      string // FULL ref: refs/heads/<branch> or refs/tags/<tag>
	Before   string
	After    string
	Actor    string // who made the upstream push — the loop guard compares it to the sync's own Actor
	Token    string // OPTIONAL short-lived credential the trigger already minted (git: installation token); never logged
	Manual   bool   // a manual /run or an initial sync reconcile (no specific push)
	Hop      int    // chained-propagation depth (bounded by the engine's hop limit)
}

SyncEvent is a provider-agnostic sync trigger. A webhook (GitHub push, Hanzo Git push) or a manual run builds one and hands it to the registered engine via Sync; the engine resolves the Syncs whose SOURCE matches (Provider, Locator/Repo) for the org and applies each. Flat + string-typed so it crosses the trigger→ engine seam without importing the engine.

type SyncFunc added in v1.801.59

type SyncFunc func(ctx context.Context, ev SyncEvent) (SyncResult, error)

SyncFunc is the reconcile entry the universal sync engine registers — a function, not an engine noun. The one implementation (clients/sync) registers it at Mount; triggers reach it via Sync.

type SyncResult added in v1.801.59

type SyncResult struct {
	Ran     int // syncs that reconciled a change
	Skipped int // syncs resolved but skipped (loop guard / idempotent / direction off)
}

SyncResult is the outcome of dispatching one SyncEvent across the resolved syncs.

func Sync added in v1.801.59

func Sync(ctx context.Context, ev SyncEvent) (SyncResult, error)

Sync dispatches one event to the registered reconcile func. Fails closed when unmounted.

type TierReaderFunc added in v1.801.256

type TierReaderFunc func(ctx context.Context, subject, namespace string) (string, error)

func TierReader added in v1.801.256

func TierReader() TierReaderFunc

nil means that subsystem isn't co-resident; apps/ leaves it uninstalled.

type Timing

type Timing = types.Timing

type TokenValidator added in v1.801.108

type TokenValidator struct {
	// contains filtered or unexported fields
}

TokenValidator verifies IAM access tokens exactly as the identity boundary does. Safe for concurrent use; the underlying JWKS cache is shared and stale-on-error.

func NewTokenValidator added in v1.801.108

func NewTokenValidator(issuer string) *TokenValidator

NewTokenValidator builds a validator bound to issuer, with the SAME JWKS endpoint SanitizeIdentity uses — jwksURLFor is the single source for both, so a token this accepts is a token the boundary accepts, and the two can never drift apart into a mint-then-refuse loop.

func (*TokenValidator) Validate added in v1.801.108

func (t *TokenValidator) Validate(raw string) (VerifiedIdentity, error)

Validate verifies raw and returns what it proved. The error is the real reason (untrusted issuer, audience, expired, no matching key) so an operator reading a failed sign-in learns which knob is wrong instead of seeing a bare refusal.

Fails closed on every path: a nil validator, an unparseable token, a token whose claims do not check out. It NEVER returns a partially-trusted identity.

type TraceSink added in v1.801.299

type TraceSink func(ctx context.Context, spans []sdktrace.ReadOnlySpan) error

TraceSink consumes a finished batch of THIS process's spans. It is what a co-resident o11y installs so cloud's own spans reach the embedded datastore with no socket and no serialization: the batch arrives live, by value.

It takes SDK spans, not collector pdata, deliberately. The pdata conversion is coupled to the o11y_index_v3 schema the exporter writes, so it belongs on the consuming side (clients/o11y/spanconv.go) — that is what keeps go.opentelemetry.io/collector out of the host.

type UsageEvent added in v1.801.256

type UsageEvent struct {
	Subject   string
	Namespace string
	USD       string // exact decimal USD ("0.00132"), never a rounded cent
	Currency  string
	Model     string
	Provider  string
	RequestID string
}

UsageEvent mirrors the ai module's payload. Separate on purpose: sharing the type would reintroduce the import.

type UsageRecorderFunc added in v1.801.256

type UsageRecorderFunc func(ctx context.Context, u UsageEvent) error

func UsageRecorder added in v1.801.256

func UsageRecorder() UsageRecorderFunc

type User

type User = types.User

type VFSClient

type VFSClient = types.VFSClient

type VaultChargeRequest

type VaultChargeRequest = types.VaultChargeRequest

type VaultChargeResponse

type VaultChargeResponse = types.VaultChargeResponse

type VaultClient

type VaultClient = types.VaultClient

type VerifiedIdentity added in v1.801.108

type VerifiedIdentity struct {
	// Owner is the validated `owner` claim — the IAM org. This is the value the
	// SuperAdmin predicate compares against the reserved admin org.
	Owner string
	// User is the canonical user id (sub, then preferred_username, then name).
	//
	// IT IS AN ATTRIBUTION KEY, NOT AN IDENTITY KEY. The fallback chain is what
	// makes it useful for a log line and unusable for a lookup: a token with no
	// `sub` resolves it to preferred_username, so two DIFFERENT subjects can
	// present the same User, and a consumer that keys a record on it can be handed
	// one subject's token and address another's row. Anything that resolves an
	// account, a wallet or a member row keys on Subject and refuses it empty.
	User string
	// Subject is the `sub` claim VERBATIM — no fallback, empty when the token
	// carries none. It is the one value that identifies exactly one IAM identity,
	// so it is the key every account lookup uses.
	Subject string
	// Username is the IAM username — the `name` half of `<owner>/<name>`.
	Username string
	// Email is the validated `email` claim, when present.
	Email string
	// IsAdmin is IAM's `isAdmin` bit: admin OF ONE'S OWN ORG. It is NOT the
	// SuperAdmin predicate — that is Owner == the reserved admin org, and
	// conflating the two is a privilege escalation. Reported so a caller can tell
	// an org admin from a plain member, never as the platform gate.
	IsAdmin bool
	// Orgs is the validated `orgs` membership-set claim — every org the subject
	// may act in (the HOME org first, then explicit team memberships), each with
	// its coarse role (owner | admin | member). It is the Slack-model tenancy set
	// a caller enumerates cross-org surfaces against (hanzo.team unions a user's
	// workspaces across it) with NO IAM round-trip. Empty on a token minted before
	// the claim shipped (iam < 1.31.34); a reader then falls back to the single
	// Owner org. Verified off the SAME signed token as Owner — never trusted raw.
	Orgs []model.OrgRef
	// Expiry is the token's own `exp`. A session built on this token must not
	// outlive it.
	Expiry time.Time
	// Audience is the validated `aud` claim — WHICH registered app IAM minted this
	// token for. Validate does not gate on it (see below), so it is published for a
	// consumer that must: a resource server narrower than the boundary — one whose
	// credential is a SESSION rather than an API call — decides for itself which
	// apps' tokens it accepts as one.
	Audience []string
	// TokenType is IAM's `tokenType` claim, which is one of the THREE things that
	// distinguish an access token from an id_token — IAM's signer emits the same
	// claim set into both but for aud/tokenType/nonce (middleware_identity.go), so
	// signature and issuer alone cannot tell them apart. A consumer that accepts a
	// bearer as a session must say which of them it means.
	TokenType string
}

VerifiedIdentity is what a token PROVED, after signature, issuer and expiry all checked out. It is deliberately the small subset a caller can act on; the authorization decision still belongs to the caller (deploy compares Owner to the admin org) and, independently, to SanitizeIdentity on every later request.

func (VerifiedIdentity) Home added in v1.801.455

func (v VerifiedIdentity) Home() string

Home is the tenant this identity acts for: the FIRST entry of the signed membership set.

IT IS NOT Owner, and the difference is a live defect class rather than a preference. `owner` carries the APPLICATION's org, so it is chosen by whichever app the caller authenticated through — a hanzo user arriving via lux-cloud presents owner="lux". The identity boundary refuses to derive a tenant from it for exactly that reason (idClaims.homeOrg, auth_identity.go), and a consumer that reads Owner as the tenant has re-opened what that accessor closed: it would scope every store query to an org the caller selected.

Empty is a REFUSAL, not a default. A token carrying no membership set is either pre-v1.33.0 or a MACHINE credential (a client_credentials app or an API key is a member of nothing), and neither is a person with a home tenant. A caller that needs one must fail closed on empty rather than fall back to Owner — falling back is the defect, spelled slightly differently.

Directories

Path Synopsis
apps
account
Package account is your own account: API keys you mint and revoke, and org onboarding.
Package account is your own account: API keys you mint and revoke, and org onboarding.
admin
Package admin is the operator's view of the fleet: orgs, users, roles, spend and system health.
Package admin is the operator's view of the fleet: orgs, users, roles, spend and system health.
admin/audit
Package audit is the /v1/admin/audit query surface, wired to cloud's REAL tamper-evident audit store (the audit.Recorder Serve builds and hands over via deps.Audit).
Package audit is the /v1/admin/audit query surface, wired to cloud's REAL tamper-evident audit store (the audit.Recorder Serve builds and hands over via deps.Audit).
admin/commerce
Package commerce is the admin cockpit's typed reader for the commerce billing plane.
Package commerce is the admin cockpit's typed reader for the commerce billing plane.
admin/core
Package core is the shared kernel of the admin subsystem: the resolved upstream clients (State) plus the one-copy business primitives every admin domain composes — the two-tier gate, the /v1 envelope writers, the tenant-scope predicate, the IAM fan-in, the single credit-grant path, the tamper-evident audit emit, and the fleet activity/time-series model.
Package core is the shared kernel of the admin subsystem: the resolved upstream clients (State) plus the one-copy business primitives every admin domain composes — the two-tier gate, the /v1 envelope writers, the tenant-scope predicate, the IAM fan-in, the single credit-grant path, the tamper-evident audit emit, and the fleet activity/time-series model.
admin/customer
Package customer is the CUSTOMER management surface (/v1/admin/customers*) — the operator cockpit's core: the live fleet customer list (incl.
Package customer is the CUSTOMER management surface (/v1/admin/customers*) — the operator cockpit's core: the live fleet customer list (incl.
admin/digitalocean
Package digitalocean reads DigitalOcean's billing and infrastructure APIs.
Package digitalocean reads DigitalOcean's billing and infrastructure APIs.
admin/finance
Package finance is the SaaS business/finance dashboard (/v1/admin/finance) — the profitability panel: what we pay every vendor (COGS), what we earn, the gross margin, how fast we're burning the DigitalOcean promo credit, and the runway that credit + burn imply.
Package finance is the SaaS business/finance dashboard (/v1/admin/finance) — the profitability panel: what we pay every vendor (COGS), what we earn, the gross margin, how fast we're burning the DigitalOcean promo credit, and the runway that credit + burn imply.
admin/health
Package health probes an upstream's health endpoint (e.g.
Package health probes an upstream's health endpoint (e.g.
admin/iam
Package iam is the admin cockpit's typed reader for the Hanzo IAM management surface (/v1/iam/ native routes).
Package iam is the admin cockpit's typed reader for the Hanzo IAM management surface (/v1/iam/ native routes).
admin/infra
Package infra is the platform's DigitalOcean fleet board: the physical inventory (DOKS clusters, droplets, block-storage volumes, load balancers) cross-referenced against what every cluster's Kubernetes actually claims, with the cost of each and an orphan analysis that is safe BY CONSTRUCTION.
Package infra is the platform's DigitalOcean fleet board: the physical inventory (DOKS clusters, droplets, block-storage volumes, load balancers) cross-referenced against what every cluster's Kubernetes actually claims, with the cost of each and an orphan analysis that is safe BY CONSTRUCTION.
admin/invoices
Package invoices is the fleet INVOICE view (/v1/admin/invoices) — every issued invoice across every tenant: number, org, amount, status, issue + due date, plus the id a future detail view fetches /v1/billing/invoices/:id with.
Package invoices is the fleet INVOICE view (/v1/admin/invoices) — every issued invoice across every tenant: number, org, amount, status, issue + due date, plus the id a future detail view fetches /v1/billing/invoices/:id with.
admin/metrics
Package metrics is the fleet SaaS-operations god-view (/v1/admin/metrics) — the operator's business dashboard: MRR/ARR, net-new vs churned MRR, the plan/category mix, the top customers, and the recent subscription movements.
Package metrics is the fleet SaaS-operations god-view (/v1/admin/metrics) — the operator's business dashboard: MRR/ARR, net-new vs churned MRR, the plan/category mix, the top customers, and the recent subscription movements.
admin/money
Package money is the admin cockpit's one billing unit: USD cents as a typed value, so no field or method anywhere has to spell "Cents" again and a dollar amount can never be silently passed where cents are meant.
Package money is the admin cockpit's one billing unit: USD cents as a typed value, so no field or method anywhere has to spell "Cents" again and a dollar amount can never be silently passed where cents are meant.
admin/revenue
Package revenue is the fleet REVENUE aggregate (/v1/admin/revenue) — the operator's money board: total prepaid balances held, total realized spend, MRR, a per-customer revenue table, ARPU, and a real spend trend.
Package revenue is the fleet REVENUE aggregate (/v1/admin/revenue) — the operator's money board: total prepaid balances held, total realized spend, MRR, a per-customer revenue table, ARPU, and a real spend trend.
admin/subscriptions
Package subscriptions is the fleet SUBSCRIPTION view (/v1/admin/subscriptions) — every tenant's plan subscription: customer/org, plan, status, monthly-normalized MRR, and the current-period start/renews.
Package subscriptions is the fleet SUBSCRIPTION view (/v1/admin/subscriptions) — every tenant's plan subscription: customer/org, plan, status, monthly-normalized MRR, and the current-period start/renews.
admission
Package admission is the launch-control GATE for Hanzo's hosted services — the COMPLETE waitlist feature, COMPOSING the ONE flag engine (apps/flags) one-way.
Package admission is the launch-control GATE for Hanzo's hosted services — the COMPLETE waitlist feature, COMPOSING the ONE flag engine (apps/flags) one-way.
ads
Package ads is your paid ad campaigns, launched and paused from one place.
Package ads is your paid ad campaigns, launched and paused from one place.
affiliates
Package affiliates is a partner program that pays commission on what your referrals spend.
Package affiliates is a partner program that pays commission on what your referrals spend.
agent
Package agent is a conversation that uses your org's own tools to get an answer.
Package agent is a conversation that uses your org's own tools to get an answer.
agents
Package agents is autonomous agents for your org: define them, run them, keep every run.
Package agents is autonomous agents for your org: define them, run them, keep every run.
ai
Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL.
Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL.
analytics
Package analytics is product analytics: send an event, read back who did what.
Package analytics is product analytics: send an event, read back who did what.
answer
Package answer is a researched answer to a hard question, with its sources cited.
Package answer is a researched answer to a hard question, with its sources cited.
ask
Package ask is a plain-language question about your business, answered with real numbers.
Package ask is a plain-language question about your business, answered with real numbers.
auditlog
Package auditlog is your org's tamper-evident audit trail: every security-relevant event, hash-chained and readable.
Package auditlog is your org's tamper-evident audit trail: every security-relevant event, hash-chained and readable.
authors
Package authors is a royalty for open-source work: your repo runs, you get paid.
Package authors is a royalty for open-source work: your repo runs, you get paid.
auto
Package auto is Hanzo Auto: build a flow from triggers and actions, publish it, and watch every run.
Package auto is Hanzo Auto: build a flow from triggers and actions, publish it, and watch every run.
automations
Package automations is workflows that run themselves, on a schedule or a webhook.
Package automations is workflows that run themselves, on a schedule or a webhook.
base
Package base is managed Hanzo Base: a hosted backend for your app — collections, records, access rules and sign-in.
Package base is managed Hanzo Base: a hosted backend for your app — collections, records, access rules and sign-in.
benchmark
Package benchmark is one honest score for any model, on the tests everyone quotes.
Package benchmark is one honest score for any model, on the tests everyone quotes.
billing
Package billing is your org's balance, what it has spent, and the cards it pays with.
Package billing is your org's balance, what it has spent, and the cards it pays with.
blueprint
Package blueprint is what a template costs to run, worked out before you deploy.
Package blueprint is what a template costs to run, worked out before you deploy.
books
Package books is double-entry accounting: chart of accounts, ledger, bank reconciliation, and the reports that prove the books balance.
Package books is double-entry accounting: chart of accounts, ledger, bank reconciliation, and the reports that prove the books balance.
bot
Package bot is your own machines, connected and ready to take a command.
Package bot is your own machines, connected and ready to take a command.
bots
Package bots is a bot doing your work on a real desktop, live, while you watch.
Package bots is a bot doing your work on a real desktop, live, while you watch.
campaign
Package campaign is one go-to-market push across paid, organic and email at once.
Package campaign is one go-to-market push across paid, organic and email at once.
captable
Package captable is your cap table: stakeholders, share classes, grants, SAFEs, rounds, and who owns what.
Package captable is your cap table: stakeholders, share classes, grants, SAFEs, rounds, and who owns what.
catalog
Package catalog is one place to browse every project, app and site built here.
Package catalog is one place to browse every project, app and site built here.
catalogsync
Package catalogsync is the REVERSE half of the storefront loop: it consumes the commerce COMMERCE stream and turns each `commerce.product.created` into ONE content.EnsureCatalogAsset call, so a newly-created catalog product gets its ecom asset rendered (design == slug) — the mirror of the forward edge (apps/content storefront.go) that publishes a rendered asset back onto the product image.
Package catalogsync is the REVERSE half of the storefront loop: it consumes the commerce COMMERCE stream and turns each `commerce.product.created` into ONE content.EnsureCatalogAsset call, so a newly-created catalog product gets its ecom asset rendered (design == slug) — the mirror of the forward edge (apps/content storefront.go) that publishes a rendered asset back onto the product image.
channels
Package channels is one inbox for the chat apps you connect — Discord, Slack, Teams, Telegram.
Package channels is one inbox for the chat apps you connect — Discord, Slack, Teams, Telegram.
cloudflare
Package cloudflare is your Cloudflare account, managed from Hanzo: zones, Pages, Workers, Workers AI, R2, KV and D1.
Package cloudflare is your Cloudflare account, managed from Hanzo: zones, Pages, Workers, Workers AI, R2, KV and D1.
cms
Package cms declares the Hanzo CMS content model as DocType fixtures on the framework engine (clients/framework).
Package cms declares the Hanzo CMS content model as DocType fixtures on the framework engine (clients/framework).
code
Package code is search and symbols across your repos, for you and your agents.
Package code is search and symbols across your repos, for you and your agents.
coding
Package coding orchestrates ONE autonomous coding run: open a live agent session, dispatch the job to the bot-gateway sandbox runtime, mirror the sandbox's progress into the session, verify the pushed branch landed in native git, open a native "PR" work item, and return a Result the caller renders.
Package coding orchestrates ONE autonomous coding run: open a live agent session, dispatch the job to the bot-gateway sandbox runtime, mirror the sandbox's progress into the session, verify the pushed branch landed in native git, open a native "PR" work item, and return a Result the caller renders.
commerce
describe.go is commerce's PROSE.
describe.go is commerce's PROSE.
commerce/transport
Package transport is the ONE seam that lets every cloud subsystem that speaks the commerce billing S2S surface (clients/{billing,account,admin, referrals,authors,affiliates,usage} + the request-edge metering gate in build.go) reach the co-resident, in-process commerce handler with a DIRECT Go call instead of an HTTP hop to the standalone commerce pod (CLOUD_COMMERCE_HTTP_URL, commerce.hanzo.svc:8001).
Package transport is the ONE seam that lets every cloud subsystem that speaks the commerce billing S2S surface (clients/{billing,account,admin, referrals,authors,affiliates,usage} + the request-edge metering gate in build.go) reach the co-resident, in-process commerce handler with a DIRECT Go call instead of an HTTP hop to the standalone commerce pod (CLOUD_COMMERCE_HTTP_URL, commerce.hanzo.svc:8001).
company
Package company is incorporation end to end: pick a structure, add founders, pay, file, and e-sign.
Package company is incorporation end to end: pick a structure, add founders, pay, file, and e-sign.
compliance
Package compliance is your KYC/KYB onboarding, accreditation records, and the evidence trail behind them.
Package compliance is your KYC/KYB onboarding, accreditation records, and the evidence trail behind them.
connectorruntime
Package connectorruntime executes an automation connector's action NATIVELY in-process — no Node.
Package connectorruntime executes an automation connector's action NATIVELY in-process — no Node.
connectorruntime/internal/bundlecmd command
Command bundlecmd is the offline connector-ingest step: it esbuild-bundles ONE ActivePieces connector source tree (its index.ts) into a single CommonJS program with the framework packages left external, and writes the blob.
Command bundlecmd is the offline connector-ingest step: it esbuild-bundles ONE ActivePieces connector source tree (its index.ts) into a single CommonJS program with the framework packages left external, and writes the blob.
content
Package content is marketing content from draft to published, on every channel.
Package content is marketing content from draft to published, on every channel.
crawl
Package crawl is any web page turned into clean markdown a model can read.
Package crawl is any web page turned into clean markdown a model can read.
crm
Package crm is your sales pipeline: the companies, the people, the deals in play.
Package crm is your sales pipeline: the companies, the people, the deals in play.
cron
Package cron is scheduled work that survives a restart: declare the schedule, then watch every run.
Package cron is scheduled work that survives a restart: declare the schedule, then watch every run.
dataroom
Package dataroom is a secure document room you share by link and watch page by page.
Package dataroom is a secure document room you share by link and watch page by page.
dataset
Package dataset is the per-org dataset plane of /v1/risk: a dataset is a VERSIONED, IMMUTABLE snapshot of one tenant's own event surface, and this is where it is declared, materialised, described, exported and disposed of.
Package dataset is the per-org dataset plane of /v1/risk: a dataset is a VERSIONED, IMMUTABLE snapshot of one tenant's own event surface, and this is where it is declared, materialised, described, exported and disposed of.
datastore
Package datastore is cloud's one connection to the analytics warehouse — the columnar store behind usage, observability and billing.
Package datastore is cloud's one connection to the analytics warehouse — the columnar store behind usage, observability and billing.
deploy
Package deploy is Hanzo CD: see what each app is running, sync it, and roll back a bad release.
Package deploy is Hanzo CD: see what each app is running, sync it, and roll back a bad release.
destinations
Package destinations is your events forwarded to the ad and analytics tools you use.
Package destinations is your events forwarded to the ad and analytics tools you use.
dns
Package dns is your DNS records: the zones and records behind every name you point at Hanzo.
Package dns is your DNS records: the zones and records behind every name you point at Hanzo.
do
Package do is the org-scoped private-network surface — /v1/vpcs and /v1/balancers — carved out of Hanzo's OWN house DigitalOcean account.
Package do is the org-scoped private-network surface — /v1/vpcs and /v1/balancers — carved out of Hanzo's OWN house DigitalOcean account.
domain
Package domain is Hanzo Domains: search a name, see the price, buy it from your prepaid wallet.
Package domain is Hanzo Domains: search a name, see the price, buy it from your prepaid wallet.
domain/namecom
Package namecom is a minimal client for the name.com Core API v4 — the wholesale registrar surface Hanzo Domains resells: check availability, price, register, renew, transfer, and set nameservers/contacts on a domain.
Package namecom is a minimal client for the name.com Core API v4 — the wholesale registrar surface Hanzo Domains resells: check availability, price, register, renew, transfer, and set nameservers/contacts on a domain.
engine
Package engine is Hanzo Engine: which models the serving runtime has loaded, and the GPUs under it.
Package engine is Hanzo Engine: which models the serving runtime has loaded, and the GPUs under it.
entitlements
Package entitlements is what your org may run: what the plan grants, and which of those products are switched on.
Package entitlements is what your org may run: what the plan grants, and which of those products are switched on.
erp
Package erp declares the ERPNext-core business model as DocType fixtures on the framework engine (apps/framework).
Package erp declares the ERPNext-core business model as DocType fixtures on the framework engine (apps/framework).
esign
Package esign is a document out for signature, signed and filed with an audit trail.
Package esign is a document out for signature, signed and filed with an audit trail.
eval
Package eval is scoring a model on your own data, with a judge you choose.
Package eval is scoring a model on your own data, with a judge you choose.
exec
Package exec is the code interpreter: run a snippet in a sandbox and move files in and out.
Package exec is the code interpreter: run a snippet in a sandbox and move files in and out.
experiments
Package experiments is A/B testing anything: a flag, an ad, a subject line, a model.
Package experiments is A/B testing anything: a flag, an ad, a subject line, a model.
explorer
Package explorer is chain data: your block indexers and how far each has caught up, plus the on-chain price feeds.
Package explorer is chain data: your block indexers and how far each has caught up, plus the on-chain price feeds.
finance
Package finance is the prepaid wallet your org pays from: deposits in, usage debits out, always balanced.
Package finance is the prepaid wallet your org pays from: deposits in, usage debits out, always balanced.
flags
Package flags is feature flags: ship it dark, then turn it on for who you pick.
Package flags is feature flags: ship it dark, then turn it on for who you pick.
fleet
Package fleet is your own compute, attached: bring a Kubernetes cluster, a GPU box or bare metal and run work on it.
Package fleet is your own compute, attached: bring a Kubernetes cluster, a GPU box or bare metal and run work on it.
flow
Package flow is Hanzo Flow: build an agent workflow on a visual canvas, run it, and read every run.
Package flow is Hanzo Flow: build an agent workflow on a visual canvas, run it, and read every run.
framework
Package framework is document types you define: describe a record once, then create, list, submit and cancel documents against it.
Package framework is document types you define: describe a record once, then create, list, submit and cancel documents against it.
functions
Package functions is your serverless code: publish it, call it over HTTP, watch every run and what it cost.
Package functions is your serverless code: publish it, call it over HTTP, watch every run and what it cost.
gateway
Package gateway is live control of the policy your API applies to every incoming request: CORS, rate limits, cache TTL and allowed methods, changed without a redeploy.
Package gateway is live control of the policy your API applies to every incoming request: CORS, rate limits, cache TTL and allowed methods, changed without a redeploy.
gateway/edge
Package edge is the runtime-mutable store for the cloud edge ("gateway role") policy: the CORS allowlist, the pre-auth per-client-IP flood cap, and the authenticated per-org rate ceiling.
Package edge is the runtime-mutable store for the cloud edge ("gateway role") policy: the CORS allowlist, the pre-auth per-client-IP flood cap, and the authenticated per-org rate ceiling.
git
Package git is Git hosting for your org: create repos, clone, push, and see what they cost.
Package git is Git hosting for your org: create repos, clone, push, and see what they cost.
goja
Package goja is the in-process JavaScript host: it runs a service's goja bundle (a self-contained, ESM-free JS file exposing globalThis.handle(req)) inside the cloud binary, with an optional tenant-bound Base/SQLite binding.
Package goja is the in-process JavaScript host: it runs a service's goja bundle (a self-contained, ESM-free JS file exposing globalThis.handle(req)) inside the cloud binary, with an optional tenant-bound Base/SQLite binding.
guide
Package guide is a step-by-step checklist that gets your business running on AI.
Package guide is a step-by-step checklist that gets your business running on AI.
help
Package help is a support desk: customers file tickets, your team answers them.
Package help is a support desk: customers file tickets, your team answers them.
iam
Package iam is Hanzo's identity provider: users, organizations, applications, and the OIDC/OAuth2 endpoints every Hanzo service authenticates against.
Package iam is Hanzo's identity provider: users, organizations, applications, and the OIDC/OAuth2 endpoints every Hanzo service authenticates against.
idv
Package idv is identity verification: run a KYC or KYB check through a licensed provider.
Package idv is identity verification: run a KYC or KYB check through a licensed provider.
index
Package index is fast full-text search over your own data, typos forgiven.
Package index is fast full-text search over your own data, typos forgiven.
ingress
Package ingress is your front door: automatic TLS certificates and hostname routing to any backend, changed live.
Package ingress is your front door: automatic TLS certificates and hostname routing to any backend, changed live.
integrations
Package integrations is how your org connects third-party accounts like Slack, and revokes them.
Package integrations is how your org connects third-party accounts like Slack, and revokes them.
k8s
Package k8s is the Kubernetes coordinates, declared once: the GroupVersionResources for our own CRs and the upstream objects we read.
Package k8s is the Kubernetes coordinates, declared once: the GroupVersionResources for our own CRs and the upstream objects we read.
kafka
Package kafka is Kafka on the platform bus: point a standard producer or consumer at :9092 and it works unchanged.
Package kafka is Kafka on the platform bus: point a standard producer or consumer at :9092 and it works unchanged.
kms
Package kms is secret custody: your org's secrets sealed at rest, plus threshold signing.
Package kms is secret custody: your org's secrets sealed at rest, plus threshold signing.
knowledge
Package knowledge is your team's wiki and your agents' memory, searchable by meaning.
Package knowledge is your team's wiki and your agents' memory, searchable by meaning.
knowledge/evernote
Package evernote normalizes an Evernote .enex export into vault.Page documents.
Package evernote normalizes an Evernote .enex export into vault.Page documents.
knowledge/lexical
Package lexical builds a Lexical EditorState JSON string from block-structured content.
Package lexical builds a Lexical EditorState JSON string from block-structured content.
knowledge/notion
export.go normalizes a Notion workspace EXPORT (the "Markdown & CSV" or "HTML" zip a user downloads) into vault.Page documents — distinct from notion.go, which shapes records from the live Notion API connector.
export.go normalizes a Notion workspace EXPORT (the "Markdown & CSV" or "HTML" zip a user downloads) into vault.Page documents — distinct from notion.go, which shapes records from the live Notion API connector.
knowledge/obsidian
Package obsidian normalizes an Obsidian vault (a tree of markdown files) into vault.Page documents.
Package obsidian normalizes an Obsidian vault (a tree of markdown files) into vault.Page documents.
knowledge/roam
Package roam normalizes a Roam Research JSON export into vault.Page documents.
Package roam normalizes a Roam Research JSON export into vault.Page documents.
knowledge/vault
Package vault is the normalized model an importer produces: a set of pages with a parent tree and wikilinks preserved inline in the body.
Package vault is the normalized model an importer produces: a set of pages with a parent tree and wikilinks preserved inline in the body.
label
Package label is the ground-truth plane: what actually turned out to be fraud, who said so, and when they could first have said it.
Package label is the ground-truth plane: what actually turned out to be fraud, who said so, and when they could first have said it.
leaderboard
Package leaderboard is the ranking of who uses AI most, in your org and globally.
Package leaderboard is the ranking of who uses AI most, in your org and globally.
legal
Package legal is the paperwork your company needs, drafted, signed and filed.
Package legal is the paperwork your company needs, drafted, signed and filed.
link
Package link is the unified AI login manager's registry: the org+user-scoped record of WHICH provider accounts (Claude Max, ChatGPT Plus, a Hanzo API key, a raw provider key) a developer has signed into, ON WHICH MACHINES, with each account's latest usage snapshot.
Package link is the unified AI login manager's registry: the org+user-scoped record of WHICH provider accounts (Claude Max, ChatGPT Plus, a Hanzo API key, a raw provider key) a developer has signed into, ON WHICH MACHINES, with each account's latest usage snapshot.
marketing
Package marketing is lifecycle email: drip sequences that reach the right people.
Package marketing is lifecycle email: drip sequences that reach the right people.
marketplace
Package marketplace is the shop for tools and agents: browse, install into your project, publish your own free or priced.
Package marketplace is the shop for tools and agents: browse, install into your project, publish your own free or priced.
meet
Package meet is the virtual office: it decides who may join a room and mints the short-lived token that lets them in.
Package meet is the virtual office: it decides who may join a room and mints the short-lived token that lets them in.
membership
Package membership is the live writer-membership source: the K8s pod poll that feeds internal/org.Source so a horizontally-scaled cloud tracks its CHANGING pod set instead of a static peer list.
Package membership is the live writer-membership source: the K8s pod poll that feeds internal/org.Source so a horizontally-scaled cloud tracks its CHANGING pod set instead of a static peer list.
metering
Package metering is how any product charges for usage: check the balance before, record the cost after.
Package metering is how any product charges for usage: check the balance before, record the cost after.
ml
Package ml is model serving: deploy a model behind an endpoint and call it.
Package ml is model serving: deploy a model behind an endpoint and call it.
mpc
Package mpc is cloud's client-side-CEK sealing client for the SEPARATE MPC node ring (ghcr.io/luxfi/mpc).
Package mpc is cloud's client-side-CEK sealing client for the SEPARATE MPC node ring (ghcr.io/luxfi/mpc).
mq
Package mq is queue and stream admin for your org: create them, watch them drain, ack what you pulled.
Package mq is queue and stream admin for your org: create them, watch them drain, ack what you pulled.
notify
Package notify is transactional email and SMS, sent through your org's own provider credential.
Package notify is transactional email and SMS, sent through your org's own provider credential.
o11y
Package o11y is your logs, metrics and traces: ship them in, query them, chart them.
Package o11y is your logs, metrics and traces: ship them in, query them, chart them.
payout
Package payout is what the referral, affiliate and author programs ask of the money plane, and it is a QUESTION: what has this org spent? That read is the qualify signal / accrual base.
Package payout is what the referral, affiliate and author programs ask of the money plane, and it is a QUESTION: what has this org spent? That read is the qualify signal / accrual base.
plan
Package plan is the plan catalog: every tier you can buy, what it costs, and what it grants.
Package plan is the plan catalog: every tier you can buy, what it costs, and what it grants.
platform
Package platform is Hanzo PaaS: deploy containers to your own tenant namespace — builds, releases, environments, logs, custom domains.
Package platform is Hanzo PaaS: deploy containers to your own tenant namespace — builds, releases, environments, logs, custom domains.
plugin
Package plugin is what each host is running, and how to change it: enable, disable, reload, or pin a service to a version.
Package plugin is what each host is running, and how to change it: enable, disable, reload, or pin a service to a version.
prefs
Package prefs is your own settings — theme, density, pinned nav — following you across every Hanzo app.
Package prefs is your own settings — theme, density, pinned nav — following you across every Hanzo app.
pricing
Package pricing is the price list: what every model, provider, GPU tier, tool and hosting plan costs.
Package pricing is the price list: what every model, provider, GPU tier, tool and hosting plan costs.
principal
Package principal is the guarantee that one org never reads another's data.
Package principal is the guarantee that one org never reads another's data.
product
Package product is the read-only inventory of the search and vector backends: /v1/search/{indexes,stats} read from Meilisearch and /v1/vector/{collections,stats} from Qdrant, reshaped into the rows the console renders.
Package product is the read-only inventory of the search and vector backends: /v1/search/{indexes,stats} read from Meilisearch and /v1/vector/{collections,stats} from Qdrant, reshaped into the rows the console renders.
projects
key.go — the project's publishable ingest key: minted with the project, resolved back to it, and the ONE thing that attributes a site's beacons.
key.go — the project's publishable ingest key: minted with the project, resolved back to it, and the ONE thing that attributes a site's beacons.
prompts
Package prompts is your prompt library, versioned, so nothing changes silently.
Package prompts is your prompt library, versioned, so nothing changes silently.
provisioning
Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app.
Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app.
pubsub
Package pubsub is your message bus: publish, subscribe, and durable streams your apps read at their own pace.
Package pubsub is your message bus: publish, subscribe, and durable streams your apps read at their own pace.
reference
Package reference is the lookup data a risk decision needs but cannot derive: which email domains hand out throwaway inboxes, which addresses belong to a datacentre or a Tor exit, which card scheme an issuer prefix belongs to, which browsers the fleet sees everywhere, and how current the designation lists the screening engine holds actually are.
Package reference is the lookup data a risk decision needs but cannot derive: which email domains hand out throwaway inboxes, which addresses belong to a datacentre or a Tor exit, which card scheme an issuer prefix belongs to, which browsers the fleet sees everywhere, and how current the designation lists the screening engine holds actually are.
referrals
Package referrals is referral ATTRIBUTION: who referred whom, and whether that referee ever became a real customer.
Package referrals is referral ATTRIBUTION: who referred whom, and whether that referee ever became a real customer.
registry
Package registry is your container and package registry: push images, pull them back, see what you store.
Package registry is your container and package registry: push images, pull them back, see what you store.
research
Package research is every experiment you have ever run, kept and comparable.
Package research is every experiment you have ever run, kept and comparable.
research/ui
Package ui embeds the Hanzo Research R&D Ops Board — the dashboard over the /v1/research evidence plane (HIP-0512) — directly into the cloud binary and serves it at /research (cloud.hanzo.ai/research + console.hanzo.ai/research).
Package ui embeds the Hanzo Research R&D Ops Board — the dashboard over the /v1/research evidence plane (HIP-0512) — directly into the cloud binary and serves it at /research (cloud.hanzo.ai/research + console.hanzo.ai/research).
risk
Package risk is HANZO RISK's model plane: the per-organisation feature surface and the per-organisation models trained on it.
Package risk is HANZO RISK's model plane: the per-organisation feature surface and the per-organisation models trained on it.
rollingcap
Package rollingcap installs the ai gate's rolling-window AI-spend cap — the Anthropic-style burst limit that resets continuously (usage older than the window drops out of the trailing sum, so there is no fixed reset boundary).
Package rollingcap installs the ai gate's rolling-window AI-spend cap — the Anthropic-style burst limit that resets continuously (usage older than the window drops out of the trailing sum, so there is no fixed reset boundary).
s3admin
Package s3admin is the one way to reach the object store: an internal client for control and data, and a public one that mints presigned URLs.
Package s3admin is the one way to reach the object store: an internal client for control and data, and a public one that mints presigned URLs.
samples
Package samples is the fleet's compute-utilization time series: ONE table, ONE writer seam, ONE read face that every compute source feeds.
Package samples is the fleet's compute-utilization time series: ONE table, ONE writer seam, ONE read face that every compute source feeds.
sbom
Package sbom is what is inside a container image: every component, resolvable by digest or image ref.
Package sbom is what is inside a container image: every component, resolvable by digest or image ref.
search
Package search is one ranked result set over everything your org has stored.
Package search is one ranked result set over everything your org has stored.
search/rank
Package rank is the ONE rank-fusion implementation in the codebase.
Package rank is the ONE rank-fusion implementation in the codebase.
security
Package security is secret scanning for your code: submit sources, get findings, masked never raw.
Package security is secret scanning for your code: submit sources, get findings, masked never raw.
security/detect
Package detect is the pure, dependency-free secret-detection engine behind Hanzo's native code-security surface.
Package detect is the pure, dependency-free secret-detection engine behind Hanzo's native code-security surface.
settings
Package settings is how an org configures each product it uses, secret fields included.
Package settings is how an org configures each product it uses, secret fields included.
share
Package share is a public URL for a service on your own machine, and a list of what you have open.
Package share is a public URL for a service on your own machine, and a list of what you have open.
sites
Package sites is your published site, live on the public web at <slug>.hanzo.app.
Package sites is your published site, live on the public web at <slug>.hanzo.app.
skills
Package skills is the skill catalogue an AI client reads to learn what it can do.
Package skills is the skill catalogue an AI client reads to learn what it can do.
social
Package social is posting to every social account you own, now or on a schedule.
Package social is posting to every social account you own, now or on a schedule.
storage
Package storage is object storage: your buckets and the files in them, with signed URLs for upload and download.
Package storage is object storage: your buckets and the files in them, with signed URLs for upload and download.
sync
Package sync is data sync: link two endpoints and keep them in step, on a webhook, on a schedule, or on demand.
Package sync is data sync: link two endpoints and keep them in step, on a webhook, on a schedule, or on demand.
tasks
Package tasks is Hanzo Tasks: durable workflows that survive a crash, with every run visible and replayable.
Package tasks is Hanzo Tasks: durable workflows that survive a crash, with every run visible and replayable.
tasks/ui
Package ui embeds the built Hanzo Tasks SPA (@hanzo/tasks, the admin-tasks app in hanzoai/admin, Vite + hanzogui) directly into the cloud binary and serves it at /tasks/* (console.hanzo.ai/tasks + tasks.hanzo.ai/tasks).
Package ui embeds the built Hanzo Tasks SPA (@hanzo/tasks, the admin-tasks app in hanzoai/admin, Vite + hanzogui) directly into the cloud binary and serves it at /tasks/* (console.hanzo.ai/tasks + tasks.hanzo.ai/tasks).
team
Package team is your org's shared workspace: documents edited together, files, seats, and agents as teammates.
Package team is your org's shared workspace: documents edited together, files, seats, and agents as teammates.
team/token
Package token mints and verifies the HS256 JWTs that the team SPA, the /v1/team/account API and the /v1/team/transactor data plane all share.
Package token mints and verifies the HS256 JWTs that the team SPA, the /v1/team/account API and the /v1/team/transactor data plane all share.
team/wallet
Package wallet embeds the built hanzo.team usage/wallet page — a SMALL @hanzo/ui@8 React static export (app/, Vite) — into the cloud binary, the same one-binary/one-origin precedent as the console (webui.go) and the tasks SPA (clients/tasks/ui).
Package wallet embeds the built hanzo.team usage/wallet page — a SMALL @hanzo/ui@8 React static export (app/, Vite) — into the cloud binary, the same one-binary/one-origin precedent as the console (webui.go) and the tasks SPA (clients/tasks/ui).
templates
Package templates is a gallery of starter kits you can deploy as they come.
Package templates is a gallery of starter kits you can deploy as they come.
tenant
Package tenant mints the key every per-org read and write is bound by, and is the only way to obtain one.
Package tenant mints the key every per-org read and write is bound by, and is the only way to obtain one.
tools
Package tools is everything your org can call, in one list: connector actions, functions, agents, skills and your own MCP servers.
Package tools is everything your org can call, in one list: connector actions, functions, agents, skills and your own MCP servers.
tracker
Package tracker is your org's issue tracker: projects, issues, and the filters to find them.
Package tracker is your org's issue tracker: projects, issues, and the filters to find them.
translate
Package translate is text in, the same text out in the language you asked for.
Package translate is text in, the same text out in the language you asked for.
treasury
Package treasury is the reserve fund behind every payout: real capital, held and accounted for.
Package treasury is the reserve fund behind every payout: real capital, held and accounted for.
treasury/cmd/anchorctl command
Command anchorctl bootstraps the Hanzo L1 (chain 36963) treasury anchor: it provisions the KMS-held signer key, funds it, and deploys contracts/TreasuryAnchor.sol — the on-chain witness that clients/treasury/anchor_evm.go later writes ledger roots to.
Command anchorctl bootstraps the Hanzo L1 (chain 36963) treasury anchor: it provisions the KMS-held signer key, funds it, and deploys contracts/TreasuryAnchor.sol — the on-chain witness that clients/treasury/anchor_evm.go later writes ledger roots to.
treasury/formance
Package formance is the Formance Ledger adapter for the treasury: it satisfies ledger.Backend by posting the reserve fund's double-entry through a live Formance Ledger service (Postgres-backed, the production ledger of record) over its v2 HTTP API.
Package formance is the Formance Ledger adapter for the treasury: it satisfies ledger.Backend by posting the reserve fund's double-entry through a live Formance Ledger service (Postgres-backed, the production ledger of record) over its v2 HTTP API.
treasury/ledger
Package ledger is the native, double-entry accounting core of the Hanzo finance stack — the store-agnostic engine that owns EVERY accounting rule (balanced postings, a non-negative reserve fund, idempotent journal entries, revenue-share math) and NOTHING about how those facts are persisted or served.
Package ledger is the native, double-entry accounting core of the Hanzo finance stack — the store-agnostic engine that owns EVERY accounting rule (balanced postings, a non-negative reserve fund, idempotent journal entries, revenue-share math) and NOTHING about how those facts are persisted or served.
treasury/ledger/sqlstore
manager.go is the PER-TENANT selector over the treasury Store: it resolves each request to its OWN Hanzo Base (SQLite) file instead of a process-wide singleton, so one tenant's finance/ledger writes can NEVER appear in another tenant's reads.
manager.go is the PER-TENANT selector over the treasury Store: it resolves each request to its OWN Hanzo Base (SQLite) file instead of a process-wide singleton, so one tenant's finance/ledger writes can NEVER appear in another tenant's reads.
usage
Package usage is what your org ran and what it cost, broken down per account.
Package usage is what your org ran and what it cost, broken down per account.
validators
Package validators is one-click validator onboarding: prove your Genesis NFT, get a node provisioned, queue its registration.
Package validators is one-click validator onboarding: prove your Genesis NFT, get a node provisioned, queue its registration.
venue
Package venue is bring your own cloud: link a DigitalOcean, AWS or GCP account and its clusters show up ready to run work.
Package venue is bring your own cloud: link a DigitalOcean, AWS or GCP account and its clusters show up ready to run work.
visor
Package visor is the compute you rent from Hanzo: machines, GPUs and clusters — launch one, resize it, tear it down.
Package visor is the compute you rent from Hanzo: machines, GPUs and clusters — launch one, resize it, tear it down.
wallets
Package wallets is blockchain key custody: create wallets, rotate their keys, and sign with them.
Package wallets is blockchain key custody: create wallets, rotate their keys, and sign with them.
webhooks
Package webhooks is how your app hears about events: register an endpoint, pick the events, get each one delivered and signed.
Package webhooks is how your app hears about events: register an endpoint, pick the events, get each one delivered and signed.
websearch
Package websearch is a web search and a page fetch your agents can call.
Package websearch is a web search and a page fetch your agents can call.
world
Package world is a live news feed filtered to what your project cares about.
Package world is a live news feed filtered to what your project cares about.
x402
Package x402 is pay-per-request over HTTP 402: quote a price, take the payment, serve the resource.
Package x402 is pay-per-request over HTTP 402: quote a price, take the payment, serve the resource.
zen
Package zen wires the hanzoai/zen serving layer into a cloud binary.
Package zen wires the hanzoai/zen serving layer into a cloud binary.
zt
Package zt mounts the Hanzo Cloud NETWORKING surface: the tenant's Hanzo Zero Trust footprint — overlay networks, their routers and mesh services — served as clean, org-scoped REST off the unified cloud binary and fronting the Hanzo Zero Trust controller (hanzoai/zt, an OpenZiti-based fabric).
Package zt mounts the Hanzo Cloud NETWORKING surface: the tenant's Hanzo Zero Trust footprint — overlay networks, their routers and mesh services — served as clean, org-scoped REST off the unified cloud binary and fronting the Hanzo Zero Trust controller (hanzoai/zt, an OpenZiti-based fabric).
Package audit is the unified cloud binary's compliance-grade audit trail — tamper-evident, append-only, and complete over the security-relevant request surface (FedRAMP AU-* / SOC 2 CC-* controls).
Package audit is the unified cloud binary's compliance-grade audit trail — tamper-evident, append-only, and complete over the security-relevant request surface (FedRAMP AU-* / SOC 2 CC-* controls).
Package brand is the white-label registry (HIP-0111): the map from a brand id (and from a request Host) to that brand's PUBLIC identity — its canonical OIDC issuer and its serving domains.
Package brand is the white-label registry (HIP-0111): the map from a brand id (and from a request Host) to that brand's PUBLIC identity — its canonical OIDC issuer and its serving domains.
Package cli is the Hanzo cloud-control CLI — the client half of the `hanzo` binary: one command tree over the Hanzo Cloud.
Package cli is the Hanzo cloud-control CLI — the client half of the `hanzo` binary: one command tree over the Hanzo Cloud.
Package clients holds the canonical ZAP-typed inter-subsystem clients used by cloud.Deps.
Package clients holds the canonical ZAP-typed inter-subsystem clients used by cloud.Deps.
cmd
cloud command
Command cloud is the Hanzo Cloud router: one binary that serves the whole API by mounting every subsystem as its own process, started on the first request that reaches it.
Command cloud is the Hanzo Cloud router: one binary that serves the whole API by mounting every subsystem as its own process, started on the first request that reaches it.
hanzo command
Command hanzo is the Hanzo control CLI: `hanzo login`, `hanzo apps`, `hanzo deploy`, `hanzo gpu connect`, and every other verb the cli package serves.
Command hanzo is the Hanzo control CLI: `hanzo login`, `hanzo apps`, `hanzo deploy`, `hanzo gpu connect`, and every other verb the cli package serves.
iam-import command
Command iam-import copies an identity store from the standalone IAM into the store the EMBEDDED iam subsystem reads (apps/iam), so cloud can serve identity in-process and the separate pod can be retired.
Command iam-import copies an identity store from the standalone IAM into the store the EMBEDDED iam subsystem reads (apps/iam), so cloud can serve identity in-process and the separate pod can be retired.
reach command
Command reach answers: does the DEPLOYED api answer every address this document publishes?
Command reach answers: does the DEPLOYED api answer every address this document publishes?
Package credz is how a cloud process gets its credentials.
Package credz is how a cloud process gets its credentials.
launch
Package launch is how a launcher tells the credential broker which app it started.
Package launch is how a launcher tells the credential broker which app it started.
Package fleet is how the light host answers a question about a subsystem: it ASKS the subsystem.
Package fleet is how the light host answers a question about a subsystem: it ASKS the subsystem.
internal
datadir
Package datadir names the on-disk data root, once.
Package datadir names the on-disk data root, once.
devmaster
Package devmaster keys a test binary.
Package devmaster keys a test binary.
fqdn
Package fqdn is the ONE place a public hostname is normalized, validated, and proven to be under a caller's control.
Package fqdn is the ONE place a public hostname is normalized, validated, and proven to be under a caller's control.
iamtest
Package iamtest is a REAL IAM issuer for tests: a keypair, a JWKS endpoint, and tokens signed with it.
Package iamtest is a REAL IAM issuer for tests: a keypair, a JWKS endpoint, and tokens signed with it.
idem
Package idem is exactly-once request execution over a per-org SQLite database: a request named by a caller-supplied idempotency key runs AT MOST ONCE, and any retry — including one re-routed to a different replica after a rolling upgrade — returns the first run's recorded result instead of executing the effect again.
Package idem is exactly-once request execution over a per-org SQLite database: a request named by a caller-supplied idempotency key runs AT MOST ONCE, and any retry — including one re-routed to a different replica after a rolling upgrade — returns the first run's recorded result instead of executing the effect again.
magic
Package magic identifies an image format from the bytes themselves.
Package magic identifies an image format from the bytes themselves.
migratetest
Package migratetest is the shared regression harness for one production bug class: a store's migrate() creates an index over a column that a pre-existing (legacy) table lacks — because the column is ALTER-added later — so CREATE INDEX fails "no such column", migrate() fails, mount fails, and the pod crashloops on deploy.
Package migratetest is the shared regression harness for one production bug class: a store's migrate() creates an index over a column that a pre-existing (legacy) table lacks — because the column is ALTER-added later — so CREATE INDEX fails "no such column", migrate() fails, mount fails, and the pod crashloops on deploy.
org
storagelock
Package storagelock refuses to boot the canonical Hanzo cloud orchestrator against Postgres.
Package storagelock refuses to boot the canonical Hanzo cloud orchestrator against Postgres.
writerlease
Package writerlease answers ONE question for ONE process: must I take the pod's writer lease before anything on this volume is opened for write?
Package writerlease answers ONE question for ONE process: must I take the pod's writer lease before anything on this volume is opened for write?
This file is HAND-AUTHORED.
This file is HAND-AUTHORED.
Package money is exact money: a USD balance carried to 18 decimals, so no amount is ever rounded away.
Package money is exact money: a USD balance carried to 18 decimals, so no amount is ever rounded away.
Package openapi projects the LIVE zip/fiber router into an OpenAPI 3.1 document.
Package openapi projects the LIVE zip/fiber router into an OpenAPI 3.1 document.
Package plane is the internal call contract: the input and output of every op one app invokes on another, and the names those ops answer to.
Package plane is the internal call contract: the input and output of every op one app invokes on another, and the names those ops answer to.
commerce
Package commerce is the typed client for the "commerce" app's internal ops.
Package commerce is the typed client for the "commerce" app's internal ops.
gen command
Command gen emits one typed client package per app that declares plane ops.
Command gen emits one typed client package per app that declares plane ops.
git
Package git is the typed client for the "git" app's internal ops.
Package git is the typed client for the "git" app's internal ops.
iam
Package iam is the typed client for the "iam" app's internal ops.
Package iam is the typed client for the "iam" app's internal ops.
index
Package index is the typed client for the "index" app's internal ops.
Package index is the typed client for the "index" app's internal ops.
integrations
Package integrations is the typed client for the "integrations" app's internal ops.
Package integrations is the typed client for the "integrations" app's internal ops.
kms
Package kms is the typed client for the "kms" app's internal ops.
Package kms is the typed client for the "kms" app's internal ops.
marketplace
Package marketplace is the typed client for the "marketplace" app's internal ops.
Package marketplace is the typed client for the "marketplace" app's internal ops.
o11y
Package o11y is the typed client for the "o11y" app's internal ops.
Package o11y is the typed client for the "o11y" app's internal ops.
platform
Package platform is the typed client for the "platform" app's internal ops.
Package platform is the typed client for the "platform" app's internal ops.
projects
Package projects is the typed client for the "projects" app's internal ops.
Package projects is the typed client for the "projects" app's internal ops.
risk
Package risk is the typed client for the "risk" app's internal ops.
Package risk is the typed client for the "risk" app's internal ops.
tasks
Package tasks is the typed client for the "tasks" app's internal ops.
Package tasks is the typed client for the "tasks" app's internal ops.
team
Package team is the typed client for the "team" app's internal ops.
Package team is the typed client for the "team" app's internal ops.
treasury
Package treasury is the typed client for the "treasury" app's internal ops.
Package treasury is the typed client for the "treasury" app's internal ops.
wallets
Package wallets is the typed client for the "wallets" app's internal ops.
Package wallets is the typed client for the "wallets" app's internal ops.
x402
Package x402 is the typed client for the "x402" app's internal ops.
Package x402 is the typed client for the "x402" app's internal ops.
Package plugin carries the fleet's build-time PROJECTION into the host: the OpenAPI subset each subsystem serves, known without running it.
Package plugin carries the fleet's build-time PROJECTION into the host: the OpenAPI subset each subsystem serves, known without running it.
account command
admin command
admission command
ads command
affiliates command
agent command
agents command
ai command
analytics command
ask command
audit command
authors command
authz command
auto command
automations command
base command
benchmark command
billing command
blueprint command
books command
bot command
bots command
campaign command
captable command
catalog command
catalogsync command
channels command
cloudflare command
code command
commerce command
company command
compliance command
content command
crawl command
crm command
dataroom command
dataset command
deploy command
destinations command
dns command
do command
domain command
engine command
entitlements command
esign command
evals command
exec command
experiments command
explorer command
flags command
flow command
framework command
functions command
gateway command
gen-app-cmds command
Command gen-app-cmds keeps the per-app command stubs in step with the fleet manifest.
Command gen-app-cmds keeps the per-app command stubs in step with the fleet manifest.
git command
guide command
help command
iam command
index command
ingress command
integrations command
kafka command
kms command
kmsreseal command
Command kmsreseal is the CR-driven re-seal migration tool for embedding the fleet KMS into cloud (#79).
Command kmsreseal is the CR-driven re-seal migration tool for embedding the fleet KMS into cloud (#79).
knowledge command
label command
leaderboard command
legal command
licensing command
link command
marketing command
marketplace command
meet command
metrics command
ml command
mq command
notify command
o11y command
o11y is the observability subsystem built as its OWN binary.
o11y is the observability subsystem built as its OWN binary.
plan command
platform command
plugins command
prefs command
pricing command
product command
projects command
prompts command
provisioning command
pubsub command
reference command
referrals command
registry command
research command
risk command
rollingcap command
sbom command
security command
settings command
share command
skills command
smoke command
Command smoke is the durable, authenticated functional smoke for the unified cloud API (api.hanzo.ai).
Command smoke is the durable, authenticated functional smoke for the unified cloud API (api.hanzo.ai).
social command
storage command
sync command
tasks command
team command
templates command
tools command
tracker command
translate command
treasury command
usage command
validators command
venue command
visor command
wallets command
webhooks command
websearch command
world command
x402 command
zen command
zt command
Package role resolves the HA role of a Hanzo Cloud process: the single authoritative WRITER (owns the RWO data dir — the ZapDB KMS store, the SQLite audit chain, the durable task store, and per-tenant SQLite) or a stateless READER (no RWO PVC, no exclusive store lock; hydrates read-only replicas from the S3/vfs replication stream and serves read paths).
Package role resolves the HA role of a Hanzo Cloud process: the single authoritative WRITER (owns the RWO data dir — the ZapDB KMS store, the SQLite audit chain, the durable task store, and per-tenant SQLite) or a stateless READER (no RWO PVC, no exclusive store lock; hydrates read-only replicas from the S3/vfs replication stream and serves read paths).
ars_types.go — minimal AutoscalingRunnerSet CRD.
ars_types.go — minimal AutoscalingRunnerSet CRD.
Package sqlpool states, once, how cloud pools connections to a SQLite file.
Package sqlpool states, once, how cloud pools connections to a SQLite file.
Package types holds the placeholder transport types AND the inter-subsystem client interfaces shared between cloud (the orchestrator) and cloud/clients (the in-process and RPC client implementations).
Package types holds the placeholder transport types AND the inter-subsystem client interfaces shared between cloud (the orchestrator) and cloud/clients (the in-process and RPC client implementations).
Package webui serves the embedded Hanzo Cloud console — the single-page app, white-labelled per request Host.
Package webui serves the embedded Hanzo Cloud console — the single-page app, white-labelled per request Host.
Package writerpin abstracts WHO holds the single-writer pin — the exclusive right to open the RWO stores for write.
Package writerpin abstracts WHO holds the single-writer pin — the exclusive right to open the RWO stores for write.
Package zapface serves the browser-facing ZAP RPC plane over WebSocket.
Package zapface serves the browser-facing ZAP RPC plane over WebSocket.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL