Identities, accounts and profiles

Profiles and specifically global profiles are a thing that atmosphere devs have discussed over and over again. We all agree that there is an issue here but people seem to disagree what to do about it. So I've come to settle this once and for all with the one true, correct opinion! ... err no of course not. But I figured I'd throw my perspective into the ring as I feel there are some things that the discussion has been missing.

What even is a profile anyways?

Probably the biggest thing I think people miss is what a profile in the context of atproto even is. We seem to be stuck a bit in the traditional app approach where an identity, an account and a profile are all more or less the same thing. But these three are quite different in the atproto context.

In the atmosphere we all have an identity based around a DID identifier0. An atmosphere identity is a DID which, per DID spec, refers to a DID subject, and resolves to a DID document which describes the DID subject. atproto additionally defines handles as a user friendly alias for a DID. DIDs are a (potentially) cross protocol/ecosystem identity. They provide a stable way to reference a potential user of the protocol0.

Accounts in the atproto context refers to an identity's existence, on a PDS, as an active participant in the atmosphere. This This is an ecosystem wide account that we use to log in to various clients to use various applications or "application modalities". It provides secure login, data storage, etc. All that generic application backend stuff.

A profile is, I would argue, an accounts existence in an app modality (similar to the relationship between account and identity). It provides an app with the rest of the info it might need to "display" or otherwise deal with a user of that app: profile picture, display name, bio, website link(s), pinned repos, etc, etc. It is an app specific thing that each app might do very different things with and which users might want to have look differently on each app. 1 -> 2 -> 3, identity -> account -> profile. A similar concept scoped at three different levels, tailor made to provide the necessary features and information to do their job at each level.

A lot of the discussion around profiles has a tendency to presuppose that a single user, a single account, has a single profile. 1 <-> 1. But I would say that's reductive. Reductive of the protocol but more importantly reductive of users. The things about myself that I want to highlight in a Bluesky profile are very different from the ones I want to highlight in a tangled profile, and the kinds of info they even can let me highlight about myself are very different. Profiles also act as a way to declare your usage of an application. With one ecosystem wide account it can be ambiguous which users use what applications. Profiles solve that. Just as an #atproto_pds service entry in a DID document and existence as an account on that PDS signals an identity's participation in the atmosphere, so does an app.bsky.actor.profile/self0 record in an accounts repo signal that accounts participation in the Bluesky app modality0. If a user doesn't have the profile for an app that user (in my opinion) shouldn't be listed in searches for users of that app and other things of that nature0. Deletion of this profile record is also a good way to signal "I no longer use this app".

I want a global profile though!

So do I! But I think we should be very intentional about what that actually means. Using the framework above a "global profile" does not exist. It cannot exist. What it seems to me that people are actually looking for is some generic place to keep the kind of info 90% of applications will want (or should want0) to have in their profile: profile picture, display name, pronouns, perhaps even website link and/or links to other social media.

This is very worth while! In my opinion this is useful in three ways:

As a place to import an initial set of values for your app specific profile record

As a record that defines the core profile data that your app specific profile record then extends

As a lexicon that apps devs can reference in their profile records to get "all the fields you probably want in a profile record"

The utility of the third option there is probably self evident if you've worked with lexicon before. But the first two are about application design.

In this model what I as a user would do is make a "global profile" that has my display name, profile picture, pronouns and perhaps some generic bio. When I log in to a new app what I would personally want to see is a pop up asking me if I want to import my profile from "[my] global profile". If I say no a new, blank app specific profile record is made in my repo, signifying that I've used this app. If I say yes the global profile will be read and it's data imported and I'm offered the choice between making this a one time import (the first option above) in which case the global profiles data will get written to the initial version of the app specific profile record, or if I want to keep my app profile and global profile synced (the second option above) in which case an otherwise blank app specific profile record is written to my repo but with a field specifying that I want to use my global profile which is then what the app will use for those value (app specific profile values still residing in the app specific profile record).

It's probably clearer to see what I mean with some example lexicon type defs. The global profile record lexicon would look something like:

{
"main": {
"type": "record",
"key": "literal:self",
"description": "A basis for an atproto profile.",
"record": {
"type": "object",
"properties": {
"avatar": {
"type": "blob",
"accept": [
"image/png",
"image/jpeg"
],
"maxSize": 1000000,
},
"labels": {
"refs": [
"com.atproto.label.defs#selfLabels"
],
"type": "union",
},
"website": {
"type": "string",
"format": "uri"
},
"pronouns": {
"type": "string",
"maxLength": 200,
"maxGraphemes": 20
},
"displayName": {
"type": "string",
"maxLength": 640,
"maxGraphemes": 64
}
}
}
}
}

and my value for that record might be something like:

{
"$type": "community.example.profile",
"avatar": { <blob> },
"website": "https://www.nel.pet",
"pronouns": "she/they/it",
"displayName": "Nelind"
}

if say ... a git forge wanted to make a profile record lexicon based on this system they might make something like:

{
"main": {
"type": "record",
"key": "literal:self",
"description": "A profile for a git forge.",
"record": {
"type": "object",
"properties": {
"coreProfile": {
"type": "union",
"refs": [
"community.example.profile",
"#atUri"
]
},
"pinnedRepos": {
"type": "array",
"minLength": 0,
"maxLength": 6,
"items": {
"type": "string",
"format": "at-uri"
}
},
"createdAt": {
"type": "string",
"format": "datetime"
}
}
}
},
"atUri": {
"type": "object",
"properties": {
"atUri": {
"type": "string",
"format": "at-uri"
}
}
}
}

I then have a few options for how my profile record for this app looks

{
"$type": "com.example.git.profile",
"coreProfile": {
"$type": "com.example.git.profile#atUri",
"atUri": "at://<did>/community.example.profile/self"
},
"createdAt": <datetime>
}

this would keep my profile for the git forge in-line with my global profile always (I think you can imagine what it would look like with the pinnedRepos field populated as well on your own)

{
"$type": "com.example.git.profile",
"coreProfile": {
"$type": "community.example.profile",
"avatar": { <blob> },
"website": "https://www.nel.pet",
"pronouns": "she/they/it",
"displayName": "Nelind"
},
"createdAt": <datetime>
}

this would be an initial population from the global profile but if I change the global profile the git forge profile wouldn't change (again, imagine the pinnedRepos field yourself).

Details not withstanding (as the above has several details I wouldn't put in production grade lexicons) this illustrates my point I think. A "global profile" is, in my opinion, best realised as a record and schema for the core properties that almost all apps will want in a profile0 that apps can then reuse from that record, as a reference in the schema of their app specific profile record and/or by pre-populating their own profile records from it.

The biggest potential downside here is the potential UX complications. I would argue that users aren't as stupid as modern application design makes them out to be but I am also no UX designer. I think gravatar and similar global profile details systems back me up here though.