HIPsHanzo Proposals
Back to HIPs
HIP-0118FinalStandards TrackSecurity

SuperAdmin & Tenant Isolation Model

Hanzo AI Team
Created: 2026-07-07
Requires: HIP-0026, HIP-0068, HIP-0111

HIP-0118: SuperAdmin & Tenant Isolation Model

Abstract

This is the one and only authorization model for the Hanzo platform. It defines a single primitive — the Org (tenant) — and exactly two admin scopes over it, which MUST NEVER be conflated:

  • Org admin (isAdmin, org-scoped) — a customer administering their own org on Hanzo IAM. Self-service. Not platform-privileged. The 99% path.
  • SuperAdmin (owner == "admin", membership of the reserved admin org) — the Hanzo platform sudo scope. The only cross-tenant identity, and the only one admitted to admin.hanzo.ai and every raw admin surface.

There is one predicate, one source of truth: an identity is a SuperAdmin if and only if its IAM owner claim equals the reserved admin org slug (admin). The edge gate, the console, and every subsystem evaluate the same predicate against the same claim. Trusting a per-org isAdmin flag for platform-level gating is a privilege-escalation bug, not an alternative implementation.

Adding a SuperAdmin is provision, never promote: an existing SuperAdmin creates a new named user in the admin org. A tenant user is never moved or promoted into admin. Together these rules make tenant isolation a property of the owner boundary and make platform privilege an auditable, individually attributable, separately-provisioned account.

HIP-0026 defines the IAM server (the Org, the owner field, the isAdmin flag). HIP-0111 defines how a client obtains and reads the owner claim. This HIP defines what those two values authorize. Where they touch, this HIP is authoritative on the authorization decision.

Reference implementation: ~/work/hanzo/gateway/cmd/admin-guard/main.go The predicate: owner == adminOrg (adminOrg default "admin", env IAM_ADMIN_ORG)

Motivation

The estate is multi-tenant IAM-as-a-service: one IAM (Auth0/WorkOS-class) serves every brand and every customer. Customers do not fork or run their own IAM; they get an org and administer it themselves. In that world the dangerous question is not "is this user an admin?" but "an admin of what?". Every serious multi-tenant breach is a confusion of those two words. This HIP exists to make the answer unambiguous and mechanical, and it is written against the specific privilege-escalation classes it prevents:

  1. Flag-for-scope confusion. A per-org isAdmin flag means "admin of my own org." If any platform surface gates on isAdmin alone, then any customer who is admin of their own org — which every self-service customer is — is admitted to the platform's cross-tenant surfaces. This is the highest-severity escalation: tenant admin → platform admin, reachable by design rather than by exploit. The fix is a different claim, not a stronger check on the same one: platform scope reads owner, never isAdmin.

  2. Self-promotion. If becoming a platform admin is a mutation on an existing tenant user (set a bit, add a role, join a group), then whoever can perform that mutation — or replay it, or find it under-guarded on one of N subsystems — escalates themselves. Provision-not-promote removes the mutation entirely: there is no bit to flip; SuperAdmin is a distinct account in a distinct org, created only by an existing SuperAdmin.

  3. Ambient dual-membership. A single human legitimately holds both a brand-org identity (their day-to-day org-admin account) and, separately, a platform SuperAdmin account. If a login silently resolves to whichever org is convenient, a routine session can acquire platform scope by accident. The model forces the admin org to be explicitly selected at authentication time and refuses to mint a platform session for any other resolved org.

  4. Silent tenant fallback. A consumer that fails to read owner and then defaults to a "default"/"personal"/empty org has just merged every unscoped request into one tenant — a cross-tenant read/write. Isolation on the owner boundary MUST fail closed (HIP-0111 §5), never fall back.

All four vanish under one primitive, two non-conflatable scopes, one predicate, and provision-not-promote. That is this standard.

Specification

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

1. The one primitive — the Org

The Org (tenant) is the only isolation primitive. Every user, application, credential, and datum belongs to exactly one Org, named by the IAM owner field (HIP-0026 schema: organization, user, application are all keyed on owner + name). Customer hierarchies (a customer's own sub-customers, B2B2C) are modeled as Orgs owned by Orgs, not as a second privilege axis. There is no tenant concept above or beside the Org. "Scope every query to owner" (HIP-0111 §5) is the concrete meaning of tenant isolation, and it is the same sentence at every layer.

2. The two scopes — never conflated

ScopePredicateMeansSurfaceWho
Org adminisAdmin == true (per-user, in that user's own org)"admin OF YOUR OWN org" — manage your org's users, apps, and sub-orgsconsole.hanzo.ai (self-service)Every self-service customer. The 99% path.
SuperAdminowner == "admin" (membership of the reserved admin org)Hanzo platform sudo — the only cross-tenant scopeadmin.hanzo.ai + every raw admin surfaceA small, named, separately-provisioned set.

The two are orthogonal, and conflating them is a defect:

  • Org admin is not a subset-or-superset of SuperAdmin; it is a different axis. A user can be an org admin of acme and hold no platform scope; a SuperAdmin is a member of admin and needs no isAdmin bit anywhere to exercise platform scope.
  • isAdmin MUST be used only for decisions within a single org (can this user edit their org's users/apps). It MUST NOT be read by any platform-level or cross-tenant gate.
  • owner == "admin" MUST be the only basis for admitting an identity to a cross-tenant or raw-admin surface.

3. The one predicate — one source of truth

An identity is a SuperAdmin if and only if:

owner == AdminOrg          // AdminOrg is the reserved org slug, default "admin"

owner is the IAM org claim carried, per HIP-0111 §5, identically in the JWT and the OIDC userinfo response, in every token format, scope-independent. The predicate is evaluated from that claim and nothing else. Concretely:

  • The edge gate (admin-guard, §7) evaluates it in decide().
  • The gateway propagates the resolved org as X-Org-Id (HIP-0519); downstream subsystems read X-Org-Id and apply the same predicate for any platform action, and scope every tenant query to it.
  • The console shows platform-admin affordances under the same predicate.

There MUST be exactly one predicate, spelled the same way everywhere. A subsystem that invents its own platform-admin test (a bespoke role name, a hardcoded email allowlist, a groups claim, a per-org isAdmin) is non-conformant even if it "works," because it drifts from the single source of truth and becomes an independent escalation surface.

Standard nomenclature is SuperAdmin. The terms "GlobalAdmin" / "global admin" MUST NOT be used in new code, config, or docs; existing occurrences (including IAM IsGlobalAdmin and the admin-guard docstrings, §7) are the legacy spelling of this exact predicate and are to be renamed to SuperAdmin — a nomenclature migration, not a logic change.

4. The reserved admin org

admin is a reserved org slug. It is not a customer tenant and MUST NOT be issued to one. Its sole purpose is to be the set whose membership is platform privilege. Its name is configurable at the edge (IAM_ADMIN_ORG) for white-label parity, but the deployed value is fixed per network and known to every gate; the default is admin. Because SuperAdmin ⟺ membership of this org, the org's member list is the platform's complete, single-surface roster of privileged accounts — the object of periodic access review (§Security, AC-2(3)).

5. Provision, not promote

Adding platform privilege is a creation, never a mutation:

  • SuperAdmin — an existing SuperAdmin creates a new named user in the admin org: iam user create --owner admin. The account is individually named and attributable. A brand-org user is never moved, copied, or promoted into admin; doing so duplicates an identity across orgs and destroys separation of duties.
  • Org admin — the org's own admin sets isAdmin on a member of their org. This never crosses the org boundary and never touches platform scope.

A human who needs both scopes holds two accounts by design: their brand-org identity (e.g. the seeded [email protected], an org admin) and a separate SuperAdmin account in admin. This dual identity is a feature — routine work carries only tenant scope; platform scope is a deliberate, separate login.

6. Surfaces — admin.hanzo.ai vs console.hanzo.ai

Two surfaces, one predicate deciding between them:

  • admin.hanzo.ai and every raw admin surface — the platform-operator surfaces: platform.hanzo.ai, studio, commerce-admin, the raw KMS admin UI, the IAM management UI. These are admitted to SuperAdmin only, gated by the admin-guard ForwardAuth middleware (§7) in front of hanzoai/ingress (HIP-0068). No raw admin surface is ever exposed without the gate.
  • console.hanzo.ai — the unified client surface for everyone: org admins and ordinary members alike, each scoped to their own org. This is where self-service lives.

The routing rule is total and dead-end-free: an authenticated identity that is not a SuperAdmin is sent to console.hanzo.ai, never shown a 403 wall on a raw admin surface (browser clients). Only a non-browser API caller receives a status code (403/401) instead of a redirect, so automation gets a clean signal. An anonymous browser is sent to interactive IAM PKCE login (HIP-0111), not to a dead end.

7. Authentication into the admin scope

Entry to the SuperAdmin scope is standard OAuth2 Authorization-Code + PKCE S256 against IAM (HIP-0111), with the reserved admin org pinned on the authorization request (organization=admin). Pinning is mandatory and load-bearing: a human who is a member of both a tenant org and admin MUST resolve to their admin identity for the platform login, otherwise the login defaults to their home org and is correctly denied. The callback MUST refuse to mint a platform session for any resolved owner != admin (it redirects such an authenticated identity to console.hanzo.ai); it mints the SuperAdmin session only when the validated token's owner equals the admin org.

Identity MAY be resolved from more than one transport (a signed edge session cookie, a Bearer/Basic JWT, an IAM SSO session), but all transports MUST collapse to the single §3 predicate. No transport may widen the scope.

Security Considerations

This model is the control that implements the following NIST SP 800-53 Rev. 5 requirements; the mapping is the compliance contract (SOC 2 / FedRAMP):

ControlRequirementHow this HIP satisfies it
AC-6(5)Privileged accounts restricted to designated personnelSuperAdmin is membership of the reserved admin org (§4), a distinct account set — not a flag on a tenant user. The privileged population is exactly the admin-org roster.
AC-5Separation of dutiesProvision-not-promote (§5): platform identity and tenant identity are separate accounts. No single account holds both scopes; the two admin scopes are orthogonal and never conflated (§2).
AC-2 / IA-2Account management; unique identification & authentication (incl. MFA)SuperAdmins are individually named users created in admin (§5), each attributable; authentication is OIDC + PKCE with WebAuthn/MFA available per HIP-0026. No shared or anonymous platform account.
AC-6(2) / AC-6Least privilege; non-privileged access for non-security functions; JIT / break-glassDual identity (§5) means routine work uses the org-admin account (tenant scope only); the SuperAdmin account is used only to operate the platform — least privilege by default, break-glass by deliberate separate login.
AU-2 / AU-12Auditable events; audit record generationThe edge gate emits the resolved org downstream (X-Org-Id) for app-side auditing of every privileged action (§7, reference impl); because privilege is a single predicate over a single claim, every privileged decision is uniform and loggable.
AC-2(3)Disable / review accountsThe admin org membership (§4) is the single review surface — one list enumerates every platform-privileged identity for periodic access review; deprovisioning is removal from that org.
SC-2 / AC-4Application partitioning; information-flow enforcementTenant isolation is the owner boundary (§1): every query is scoped to owner, the gateway propagates it as X-Org-Id, and cross-tenant flow exists only under the single SuperAdmin scope. Reading owner MUST fail closed, never fall back to a default org (HIP-0111 §5).

Additional considerations:

  • Fail closed on the boundary. Any consumer that cannot resolve owner MUST reject the request. Falling back to a "default", "personal", or empty org silently merges tenants and is a reportable isolation defect (SC-2/AC-4).
  • API vs browser asymmetry is intentional. Non-browser callers fail closed with a status code; browsers are redirected to the correct surface. Neither path ever widens scope; the asymmetry is only in how denial is delivered.
  • The admin org name is not a secret. Security rests on IAM authentication and org membership, not on the obscurity of the slug. IAM_ADMIN_ORG is configuration for white-label parity, not a credential.
  • No customer runs IAM. Customers are tenants of one multi-tenant IAM; they cannot mint their own admin-org membership because they do not operate the issuer.

Reference Implementation

~/work/hanzo/gateway/cmd/admin-guard/main.go is the canonical enforcement point: a single ForwardAuth gate consumed by hanzoai/ingress (HIP-0068) at GET /__guard/verify, in front of every raw admin surface. It is the concrete, production form of §3, §6, and §7.

  • The predicate (§3). decide() admits the caller only when owner != "" && owner == c.adminOrg; on match it sets X-Org-Id and X-Admin-Guard: allow and returns 204. adminOrg is loaded from IAM_ADMIN_ORG, default "admin". This is the one source of truth; the docstring states it verbatim: "ONE predicate, one source of truth."
  • Dead-end-free routing (§6). An authenticated non-SuperAdmin browser is redirected to console.hanzo.ai (302); a non-browser caller gets 403 "global admin required"; an anonymous browser is sent to IAM PKCE login. No raw admin 403 wall for browsers.
  • Org pinning + callback refusal (§7). startLogin() sets organization=adminOrg on the authorize request so a dual-member resolves to their admin identity; handleCallback() refuses to set a session when owner != adminOrg and redirects to the console instead.
  • Transports collapse to one predicate (§7). Identity is resolved from three orthogonal sources — the guard's signed session cookie, a Bearer/Basic JWT validated through iamauth (the JWT already carries owner, so no IAM round-trip), and an IAM session cookie resolved via get-account — all funneling into the same owner == adminOrg test. No source widens scope.

Nomenclature migration (§3). The file currently uses the legacy spelling — the docstring says "GLOBAL ADMINS ONLY" and refers to IAM IsGlobalAdmin. The logic is already this standard exactly (owner == c.adminOrg); the identifiers and prose are to be renamed to SuperAdmin (IAM IsGlobalAdmin → IsSuperAdmin, console useIsGlobalAdmin → useIsSuperAdmin). This is a rename, and it changes no decision.

References

  1. HIP-0026: Identity & Access Management Standard — the IAM server: the Org primitive, the owner field, the isAdmin flag.
  2. HIP-0111: Hanzo IAM Authentication Standard — how a client obtains and reads the owner claim; the fail-closed tenant rule (§5).
  3. HIP-0519: One Identity Boundary — JWT validation and X-Org-Id propagation to subsystems.
  4. HIP-0068: Ingress Standard — the ForwardAuth mechanism the admin-guard plugs into.
  5. HIP-0027: Secrets Management Standard — KMS-managed secrets for privileged surfaces.
  6. ~/work/hanzo/gateway/cmd/admin-guard/main.go — the reference implementation of the predicate and the two surfaces.
  7. NIST SP 800-53 Rev. 5 — AC-2, AC-4, AC-5, AC-6, IA-2, SC-2, AU-2, AU-12 (the control families mapped above).
  8. FedRAMP and SOC 2 — the compliance regimes the NIST mapping serves.

Copyright

Copyright and related rights waived via CC0.