One account. A face for every app.
Your ATProto account signs you in everywhere, but every app keeps its own profile of you. Omote shows you where each one comes from, and lets you change a profile in one app, or in every app at once.
Which profile does each app show?
Change your name on Bluesky, and does it change on Grain? Nothing tells you. Grain's own account holds nine profile records, one per app, with three different display names between them.
Omote gives every app a way to say where its profile comes from, and gives you one place to see it. In Noh theatre, omote (表) are the masks a performer changes between roles while the audience still knows who is underneath.
How it works
Three steps, in your own account.
- 1
Sign in with your handle
Any ATProto account works. You confirm on your own server, and omote stores nothing: your profiles stay in your account.
- 2
See where you appear
Every app's profile of you, side by side, and for each field the profile it comes from.
- 3
Change it here, or everywhere
Edit one app's profile within that app's own rules, or your shared profile, which every app that builds on it follows.
For ATProto developers
Your lexicon. One more field. The same answer in every app.
One field in your lexicon
Add extends to your profile record, and it builds on the profiles a person already has. Records take part by shape, so your lexicon never depends on ours.
Resolves like tsconfig
Later bases win, the record's own fields win over all, and null hides what the bases say. The rules are written down, so every app lands on the same answer.
No AppView in the path
@omote-social/profiles reads straight from the account's own server, in browsers, Node and Cloudflare Workers.
{
"$type": "social.grain.actor.profile",
"extends": ["app.bsky.actor.profile", "social.omote.actor.profile"],
"displayName": "Alice (photos)",
"description": null
}import { getProfile } from "@omote-social/profiles";
const profile = await getProfile("alice.example.com", "social.grain.actor.profile");
profile.displayName; // "Alice (photos)": set by Grain's own record
profile.sources.avatar; // { collection: "app.bsky.actor.profile" }Choose the face you show.
One account, as many profiles as you use, and never a question of which one an app shows.