Cross-Tenant Identity
When the same HGE ID is a member of multiple tenants, the parts of the user profile that describe who you are — your avatar, theme, language — are shared across all of them. Everything that describes what you can do in this particular tenant (permissions, favorites, saved views, dashboards) stays tenant-scoped.
What is shared
| Field | Scope | Stored in |
|---|---|---|
| Name | Global | HGE ID (synced into every tenant row) |
| Global | HGE ID (read-only in the app) | |
| Avatar | Global | user_global_avatars (one file/HGE ID) |
| Theme | Global | user_global_prefs (key theme) |
| Locale | Global | user_global_prefs (key locale) |
| Favorites | Per tenant | user_prefs (tenant-scoped) |
| Saved views | Per tenant | user_prefs (tenant-scoped) |
| Permissions | Per tenant | user_groups / user_group_members |
How the avatar is stored
Uploading an avatar replaces the single row in user_global_avatars keyed by
your HGE ID. The file lives at
/data/uploads/_global/avatars/<avatar_key>.webp, where avatar_key is an
opaque random UUID generated on every upload. The URL never contains your
HGE ID; instead the frontend calls:
GET /api/uploads/avatar/by-key/<avatar_key>
The endpoint requires a valid HGE ID JWT — any authenticated user can fetch
any avatar, just like a corporate directory. Files are re-encoded through
sharp to a 256x256 WebP, stripping EXIF and any embedded payloads.
Global avatars are not counted against any tenant's storage quota. A hard 100 KB post-encode ceiling keeps the global pool bounded.
Identity badges
The user profile and the avatar dropdown both show up to two badges:
- "Member of <tenant>" appears whenever you are accessing a non-master tenant. It tells you which tenant owns the master row for your HGE ID.
- "Managed by <tenant>" appears whenever Domain Governance has been enabled for your email's domain. The governing tenant can enforce policies on your account (block cross-tenant joins, restrict profile edits, etc.).
When both apply, both badges render side-by-side.
Theme and language
Switching theme or language calls PUT /api/user-prefs/theme (or /locale).
The backend routes those two keys to user_global_prefs (HGE-ID-scoped); any
other key continues to write into the tenant-scoped user_prefs table.
On boot, the LocaleProvider and ThemeProvider read the global preference and override the local cookie value if they differ. The cookie is the immediate authority for unauthenticated pages (marketing site, docs) so a single sign-out doesn't reset your theme.
What is NOT synced
Anything that points at tenant-specific entities stays tenant-scoped:
- Favorites that reference tenant-specific assets, users, or saved searches.
- Dashboard layouts.
- ITSM saved queries.
- Notification read state.
Switching tenants always shows the right tenant's data; only the cosmetic layer (avatar, language, dark mode) follows you everywhere.