Hanzo AI

Base

Managed Hanzo Base: a hosted backend for your app — collections, records, access rules and sign-in.

Also for this capability: API · CLI · MCP · SDKs

Managed Hanzo Base: a hosted backend for your app — collections, records, access rules and sign-in.

Base URLhttps://api.hanzo.ai
Operations3
AuthAuthorization: Bearer $HANZO_API_KEY

Specification

HIP-1104 · Base — The Hosted Backend — Draft · read the specification →

/v1/base is managed Hanzo Base: a hosted backend for an app — collections, records, access rules and sign-in — one engine instance per org, each on its own SQLite. It also serves the platform's public waitlist at /v1/waitlist. It is implemented in hanzoai/cloud at apps/base, embedding the github.com/hanzoai/base engine in-process.

Motivation

The engine used to run as its own pod, whose whole job was constructing a Base and serving it, and a third prefix forwarded to a separate managed deployment for the sake of a cross-instance registry that could only ever answer anonymously — it held zero rows for its whole life, while two engines answered one question from two disks. The embed collapses that to one engine, in-process, per org (apps/base/base.go).

Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.

§1 Two lanes, deliberately not one app

Lane 1 is the waitlist: ONE platform Base instance carrying the waitlist plugin, public because a signup surface has no principal to scope by, platform-owned because it is one waitlist per brand rather than customer data. Lane 2 is hosted Bases: ONE Base instance PER ORG, opened lazily and pooled. The lanes never overlap — the waitlist plugin binds a fixed /v1/waitlist regardless of the engine's mount prefix.

§2 The store

Per-org SQLite under {DataDir}/base/{TenantSegment}/, the platform waitlist under a _platform segment whose leading underscore is outside the segment alphabet so it can never collide with an org (apps/base/base.go:96-100). The org→directory encoding is the fleet's one injective, traversal-safe tenant encoder, so an org maps to exactly one physical identity everywhere in the binary (apps/base/pool.go:34-40). Every store is single-open, single-writer, on the one RWO volume, single-replica by deployment — the durability the standalone pod had, without the pod.

§3 The pool

Instances open on first request, migrate once, and are pooled: LRU-capped at 64, idle-evicted after 10 minutes, both env-overridable. Only IDLE instances are ever closed — an acquired instance is pinned for the request, so eviction can never yank one out from under a live request (apps/base/pool.go:23-31).

§4 Addresses

GET /v1/base/health is the one typed operation. Everything else under /v1/base/* is the embedded engine's own REST surface, relayed through the org's own mux — the engine owns that route table, and typing it in cloud would put a second copy here, free to drift (the same shape HIP-1062 states for the durable engine). The engine's API prefix is pinned to /v1/base so even its self-generated URLs live under the capability's address.

§5 Tenancy and identity

The org is resolved from the validated principal and refused when absent (apps/base/base.go:288-290); the request is then served by that org's own instance, so cross-tenant reads are closed physically — the other org's rows are not in the file being queried. Each per-org instance validates bearers against Hanzo IAM's JWKS as its EXCLUSIVE auth source: the edge selects the org, Base authorizes the record, both consume ONE IAM and no second auth path exists.

§6 Activation, money, events, telemetry, stage, upstream

The embed activates only when CLOUD_BASE_EMBED is truthy; absent it, Mount is a health-only no-op, so linking this subsystem everywhere changes nothing until a single-writer deployment opts in. Free (plugin/base/main.go, cloud.Free). It publishes nothing to the bus. Beyond the request span it emits structured log lines only. Stage ga: the hosted backend is the data plane of the self-service core. Its upstream is github.com/hanzoai/base v1.5.65 (go.mod:691), MIT-licensed with the original author's notice preserved in its LICENSE; the engine survives whole — this app adds the pool, the tenancy resolution and the mount, not a fork of the engine's internals.

Rationale

The alternative to per-org files is one engine with a tenant column, which works until one query forgets the predicate. The alternative to the pool is an instance per org held open forever, which trades a cheap reopen for unbounded memory. Both alternatives were live in the estate — the forwarding prefix and the standalone pod — and both are what this design deleted.

Security Considerations

The engine is a full backend — auth, rules, file storage — so the dangerous wrong implementation is a tenancy short-circuit around it: a request routed to the wrong org's instance is a whole-backend disclosure, not a row leak. The injective segment encoding and the principal-only org resolution are the two facts that prevent it; neither takes any input a caller controls. The public waitlist lane holds no principal by design, and therefore holds no customer data — its knobs and secrets resolve from KMS at boot, never in code.

Four surfaces

SurfaceReaches this capability asCoverage
RESTbase at its own prefix3 operations
CLIhanzo base …3 of 3
SDKBaseApi in every published client3 methods
MCPtool base on https://api.hanzo.ai/v1/mcp1 operation

Quickstart

export HANZO_API_KEY=sk-...   # console.hanzo.ai → API keys

Then the first call — a read that needs nothing but the key. GET /v1/base/bases, operation get_base_bases:

hanzo base bases list

Answers 200 with baseView[] — ok.

Endpoints

EndpointWhat it does
GET /v1/base/bases/{org}Describes ONE org's Base — whether its store exists, and what it occupies.
GET /v1/base/basesLists every Base the caller can reach, one per org their token carries.
GET /v1/base/healthReports that the base subsystem is serving.

All Hanzo APIs · Interactive reference

How is this guide?