Portrait
Portrait is Intrasys's self-hosted, Gravatar-style avatar service. One avatar per person, consistent across every Intrasys app (Panes, Presto, Prism, …), keyed by a hash of the person's email.
Everyone gets a deterministic generated avatar by default — no account, nothing stored. Anyone can sign in to Portrait's own console to design their avatar in a full option editor, upload a real photo, or ✨ Ask AI to suggest a designed avatar from a photo. Once saved, that image is what Portrait serves everywhere.
A consuming app does nothing but render an <img>. No SDK, no per-app secret, no schema change.
The problem it solves
Every Intrasys app renders a user avatar — sidebar, member lists, profile. Today each shows initials
on a coloured chip. There is no shared, real avatar, and WorkOS can't provide one: its
profile_picture_url is read-only and only populated by a social IdP, so magic-auth users (the house
login pattern) have none.
Portrait fixes this with three ideas:
- Email hash as the shared key — an app already knows its user's email, so it computes the key itself and builds the URL. It never calls a Portrait API or holds a Portrait secret. This is what makes adoption free.
- Always return something — an unknown hash gets a deterministic generated avatar, already better than an initials chip, so an app can adopt Portrait before a single user has an account.
- The read path never touches the database — a read is one object-store lookup; hit serves, miss generates from the hash. The DB can be down and not a single avatar breaks anywhere in the estate.
Who it's for
Every end-user of every Intrasys app, and every app that renders an avatar. Users manage one face in one place; apps adopt it in one line.
Core concepts
- Identity key —
sha256(lower(trim(email))), byte-identical to Gravatar's spec. Public, not a secret, safe in client-side HTML. - Three states for any hash — generated (nothing saved), designed (saved option config, incl. ✨ Ask AI output), or uploaded (a real photo).
- Accounts & claims — an account anchors on a WorkOS user; emails are claims attached to it. One person can claim work + personal addresses and every verified one resolves to the same avatar.
Status
Shipped and live. All four apps run — the public read path (media.*), the session API (api.*),
the console (console.*), and the marketing site (portrait.intrasys.ai):
- Public read path — deterministic generated avatars, sizes, PNG/SVG, caching, metadata.
- Console — login, avatar designer, photo upload, ✨ Ask AI (photo → suggested design), email claims.
- Session API — save, reset,
from-photo, email-claim management.
An app can adopt Portrait in one line today.
Next steps
- Getting started — set your avatar, adopt it in an app.
- Concepts — the identity key, three states, accounts & claims, the read path.
- App integration — the one-line
<img>, fallbacks, caching. - API reference — the
media.*read API andapi.*write API.