HIP-0134: One Process, One Socket, One Identity
Abstract
Every Hanzo service is a plugin of the cloud binary (HIP-0106, HIP-0106).
Plugins reach each other over ZAP on unix domain sockets. IAM is the only
thing that reads a JWT, and the principal it establishes is what every other
plugin uses.
That is the architecture. This HIP states it, and states its three consequences — each of which is a thing that does not exist:
- No second listener. A plugin serves its own socket. There is no ZAP TCP port to reach it on, so there is no wire to authenticate or encrypt.
- No second name. The binary is
cloud. There is nocloud-api. - No second identity path. IAM owns the principal. Nothing re-derives identity from headers, no edge process mints an identity-header set, and no gate in front of a plugin decides who may read what.
Specification
1. Transport: a socket per plugin
A plugin listens on exactly one address — its unix socket. The host hands it that address; the plugin does not choose a port.
{CLOUD_DATA_DIR}/run/<plugin>.sock # default /run/hanzo/<plugin>.sock
The wire is ZAP (HIP-0114, HIP-0120). A plugin's routes ARE its ZAP surface
(HIP-0122), so there is no second registry of methods and no second
serialization. zip selects the ZAP transport for a bare address, which is
what a socket path is.
A caller resolves a peer by name, never by address:
cloud.Dial("treasury").As(c).Get(ctx, org, "/v1/treasury/reserve", &out)
Resolution happens per call, so a plugin that starts later is picked up without a restart. A missing socket is the answer "that plugin is not running here", returned as an error — never a zero value.
A plugin MUST NOT open a TCP listener. The only TCP listeners in a deployment are the edge HTTP listener, terminated by ingress, and the ops listener for health and metrics (HIP-0113).
2. Authentication: the kernel, then IAM
Two facts do all the work.
The kernel proves the peer. A 0600 socket in the run directory is reachable only by processes on the same host running as the same user. There is no network path to it. This is the entire authentication of the internal plane: no credential to mint, distribute, rotate, or revoke, and nothing that can fail open.
IAM proves the human. The IAM plugin validates the JWT and establishes the principal — org, user, admin — once, in one place.
A call carries the principal IAM established. Because the socket bounds who may speak, carrying it is delegation rather than assertion: a caller passes on authority it already holds, and the callee still applies its own rules. There is no way to obtain a principal except from IAM.
Nothing sanitizes, re-derives, or re-validates a principal IAM established. A component that re-checks identity is a second implementation of IAM, and two implementations of "who is the caller" do not double the assurance — they create a disagreement, and the weaker one is the one an attacker uses.
3. Authorization: in the plugin, on the principal
The plugin that owns the data decides who may see it, reading the principal and filtering server-side on every query.
Org scope by default. A request for org X returns org X's rows. Not a UI filter, and not a query the client can widen.
SuperAdmin is the only cross-org scope, and it is ONE predicate
everywhere: owner == "admin", membership of the reserved admin org. An
org admin (isAdmin inside their own org) has no platform authority.
Conflating the two is privilege escalation.
An edge gate may answer "is this a valid session" — that is authentication, relayed from IAM. A gate that decides which rows you may see has taken the plugin's job, and the plugin will never grow the ability to do it correctly.
4. Naming
The binary is cloud. A second Service name for the same pods is a second
address with different properties.
One IAM application per guarded surface, named for the surface:
hanzo-admin for admin.hanzo.ai, hanzo-platform for platform.hanzo.ai,
hanzo-ci for ci.hanzo.ai. An application named for a component — a guard, a
proxy — that fronts several surfaces makes them indistinguishable in the
audit log and gives them a shared blast radius. Redirect URIs are the
surface's own callback exactly; no wildcards.
Changing a surface's application is create-new → cut the surface over →
verify login → retire old. An in-place rename invalidates every live token
whose aud names the client.
Security Considerations
Socket permissions are the perimeter. The run directory is writable only by the service user, sockets are 0600, and the run directory is never a shared volume another tenant's workload can mount. A world-readable socket is equivalent to an open unauthenticated port.
Delegation is not escalation. As() forwards the principal a caller
already holds; the callee applies its own rules. Nothing in the internal
plane grants authority a caller did not arrive with.
Shared build fabric is a separate boundary. Running another tenant's code — CI — on a runner that mounts a registry credential, shares a docker daemon, or reuses state across jobs is a tenancy violation regardless of the identity plane. A job running foreign code gets an ephemeral runner, no long-lived credential, and no shared daemon.
What an attacker must achieve. With the internal plane on sockets, a cross-tenant read requires code execution as the service user on the host — not a forged header, not a reachable port, not a stolen bearer token.
Reference Implementation
hanzoai/clouddial.go— peer resolution by name, ZAP over the callee's socket, delegation viaAs, errors that name the transport they tried.hanzoai/cloudserve.golistenOn— a plugin honours the host-provided socket address and serves that alone.hanzoai/ziptransport.go— the scheme table: a bare address selects ZAP, which is why a plugin needs no transport configuration.hanzoai/iam— the principal's single owner.
References
- HIP-0026 — Identity & Access Management standard
- HIP-0106 — Cloud: unified Hanzo binary
- HIP-0113 — Ops listener: health, readiness, metrics
- HIP-0114 — ZAP inter-VM cognitive transport
- HIP-0106 — Plugin & VM model
- HIP-0120 — ZAP-native transport, gRPC elimination
- HIP-0122 — zip: the ZAP-native application server core