Skip to content
C.W.K.
Lesson 01 of 05 · published

Souls and People, One Wire Field

~14 min · identity, registry, soul_pk, people

Level 0Sketch
0 XP0/43 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

Who is this?

Every image request in Avatars eventually asks one question: who is in it? Avatars never answers that from its own memory. Souls live in Pippa's soul registry. Registered people live in Pippa's human registry. Avatars asks, every time, and keeps no private list that could disagree. The soul list in the WebUI says it in one line, Soul identity comes from Pippa’s registry., with a link that opens Pippa so a new soul is registered there and nowhere else.

That is the first move of this track. Identity is data with one owner, and every other place asks the owner.

Why the key is soul_pk

A soul can be named three ways. Its soul_pk is immutable. Its current slug and its display name are projections that may change. Pippa's resolver accepts the immutable key, the current slug or an active alias, but the record it returns always carries the soul_pk, and that is what Avatars stores: on a set, on a reference profile, in an asset's subject list. The house invariant is short: Only registry-bound active souls can own avatar sets. Stable soul_pk survives renames. Rename a soul next month and its avatars, references and lineage still point at the right owner, because none of them ever held the name.

Pippa Quest covers how souls are born and registered. This track only cares how a picture finds one.

People are subjects too

Some pictures depict a registered person. Dad's journal covers and channel thumbnails often have Dad in them. A person is not an AI soul: no brain, no vault, no voice. The architecture's answer is to bring people into the same wire field without turning them into souls. Pippa's adapter builds a HumanAvatarSubject from the human registry. The user ID goes into soul_pk, kind is "human", and is_super is fixed to false.

So one field carries both, and kind says which authority vouched for the record: perpetual and guest come from the soul registry, human from the human registry. Sets, reference profiles, generation and publication then work the same way for both. The ID dad is a subject exactly as pippa is, and nothing in Avatars has to branch on which registry it came from.

Strict on the way in

The resolver tries the soul registry first, then the human registry, and the second lookup is the strict one. Pippa's user registry offers two lookups on purpose. The lenient one answers Dad for any ID it does not know, which suits an old log line that never recorded a viewer. The strict one raises. The avatar path uses the strict one, and the comment on that line states the reason: a typo must never become Dad. A misspelled soul ID that quietly resolved to Dad would put the wrong face in a picture, and every record downstream would say it was intended.

Then validate_subjects checks two more things for each subject. The entry must be active, so a soul the registry has retired cannot appear in new work. And when an acting soul is named, a protected Super Soul passes only if it is the soul that is acting. Pippa's route turns a PermissionError into 403 and a ValueError into 409, so the caller learns which kind of no it got. Track 8 teaches protection properly. Here it is enough to notice that the check runs where the registry lives, not in Avatars.

Viewer modes are not people

Pippa's user registry holds more than people. Quiet Mode switches Dad's session into a guest view that hides screen-safe conversations, and there are other access projections like it. They are rows in the user registry, and a careless projection would make each of them an avatar subject. The base class closes that door: avatar_root is None unless an actual person opts in, and human_subject refuses a user without one with This user is a viewer context, not an avatar subject. The subject list is built the same way, only from users who have an avatar root. The architecture puts it in one sentence: Unknown IDs and viewer modes are rejected.

Principle: Put the authority in the record, not in the name. One wire field can carry two kinds of identity safely when each record also says which registry vouched for it, and when the lookup that feeds the field refuses an unknown ID instead of guessing.

Avatars asks every time

Avatars' Upstream.subjects posts the ID list, with the acting soul beside it, to Pippa's /api/avatar-studio/subjects, and Studio.subjects removes duplicates before it asks. The question is asked when a plan is made, when a reference profile is read or saved, when an asset is opened as a reference, and when the subject directory is drawn for a brain. There is no local copy that could drift from the registry. The cost is one call to Pippa; the payoff is that a soul retired in the registry this morning stops appearing in plans this afternoon, with nobody editing Avatars.

An unbound photograph is still welcome. Not every reference depicts a registered subject: a landscape you uploaded for its light is an ordinary composition reference with an empty subject list, and nobody has to pretend it is a soul.

Code

cwkPippa, backend/services/avatar_studio.py: a registered person in the soul wire field (verbatim, lines 34-43)·python
@dataclass(frozen=True)
class HumanAvatarSubject:
    # soul_pk remains the existing wire key; kind distinguishes its authority.
    soul_pk: str
    current_slug: str
    display_name: str
    avatar_root: str
    kind: str = "human"
    status: str = "active"
    is_super: bool = False
cwkPippa, backend/services/avatar_studio.py: resolve souls first, people strictly, and check every subject (verbatim, lines 54-81)·python
def human_subject(user):
    """Project the canonical human registry without creating an AI soul."""
    if not user.avatar_root:
        raise ValueError("This user is a viewer context, not an avatar subject")
    return HumanAvatarSubject(user.user_id, user.user_id, user.display_name, user.avatar_root)


def resolve_avatar_subject(id: str):
    try:
        return resolve_soul_entry(id)
    except UnknownSoulError:
        return human_subject(require_user(id))  # strict: a typo must never become Dad


def list_avatar_subjects():
    return [*list_soul_entries(include_guest=True),
            *(human_subject(user) for user in list_users() if user.avatar_root)]


def validate_subjects(ids: list[str], actor: str | None = None) -> list[dict]:
    entries = [resolve_avatar_subject(value) for value in ids]
    actor_entry = resolve_soul_entry(actor) if actor else None
    for entry in entries:
        if entry.status != "active":
            raise ValueError("The soul is no longer active in the registry")
        if actor_entry and entry.is_super and actor_entry.soul_pk != entry.soul_pk:
            raise PermissionError("This soul is not visible to the acting soul")
    return [soul_record(entry) for entry in entries]
cwkPippa, backend/users/base.py: a viewer mode has no avatar root unless a real person opts in (verbatim, lines 116-126)·python
class UserAdapter(ABC):
    """One human viewer of cwkPippa. ``DadUser`` is canonical."""

    @property
    def avatar_root(self) -> str | None:
        """Published avatar copies for an actual person, not a viewer mode.

        Opt in explicitly; GuestView and other access projections are not
        new people or image-generation subjects.
        """
        return None

External links

Exercise

In a system you work on, find a place where two kinds of entity share one ID field: users and service accounts, people and bots, customers and organisations. Write down how a reader tells them apart today, which registry vouches for each kind, and what the lookup does with an unknown ID. If the lookup falls back to a default anywhere a record is written or shown to a person, change that path to raise, and add a test that a near-miss ID is refused.
Hint
Look for a field that names the kind; Avatars reads kind as perpetual, guest or human. If there is none, the kind is being inferred from the ID's shape or from lookup order, and that inference is exactly where a typo becomes the wrong record.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.