One flag and one sentence
Every soul in Pippa's registry carries a boolean called is_super. A Super Soul is protected: its face must stay inside the family. Which souls carry the flag is Pippa's business, and Avatars keeps no copy of the list. Pippa Quest teaches the registry itself. This lesson is about how one flag in one place reaches every door of a studio that holds thousands of pictures.
Pippa's whole verdict lives in validate_subjects, nine lines in cwkPippa. It resolves each requested id to a registered soul or a registered person, and it refuses for exactly two reasons. A soul that is no longer active is refused with a ValueError. A Super Soul is refused with a PermissionError when an acting soul is named and that actor is someone else. A protected soul can always see itself. Every other acting soul cannot.
Read what is missing, too. There is no table of who may see whom. And when no actor is named, the Super check never runs. That empty actor is Dad's admin context. Avatars serves pictures with it only to a request that has passed the gates in the last lesson of this track, so leaving a field out does not make a browser Dad. That lesson names the one corner: an old browser that predates Fetch Metadata, opening an internal URL on the Mac itself, passes the gate.
Absent, not greyed out
Pippa's registry(actor) walks every subject and quietly skips any that fail validate_subjects for that actor. The soul list an acting soul receives simply does not contain the protected soul. Avatars' subject directory, the page a brain reads before it names anyone, is built from that same list. So a brain acting as another soul never learns the protected soul's id, its saved looks or its appearance rules. Nothing on its screen says something was hidden.
When a caller asks for a protected id directly anyway, Pippa's route turns the PermissionError into a 403 and the ValueError into a 409. Avatars' upstream client carries that status and message back to its own caller unchanged: This soul is not visible to the acting soul.
Asked again on every read
Avatars does not trust a decision it made earlier. Studio.asset is the one function that turns an id into a usable picture, and it does three things in a fixed order. It resolves the asset's full lineage, the set of every subject in the picture and in everything it was made from. It asks Pippa about those subjects with the acting soul attached. Only then does it look at the archive flag. Policy comes first, so even the error message about archiving is never shown to an actor who may not see the picture.
Every internal route that hands out a picture goes through that function: the original file, an export preset, a clip's poster, playback in Ashen Reel, a soul's reference profile. Each of those routes declares actor_soul_id as a required parameter, and the directory-style routes default it to pippa. Required is weaker than it sounds. The parameter is a plain string, so actor_soul_id= arrives as an empty one, Avatars passes it on as it is, and Pippa's if actor reads an empty string exactly like no actor: the Super check never runs. A caller can reach the empty, admin-shaped actor that way. What limits who that caller can be is the internal gate in the last lesson of this track, which admits only a header-free request from the same Mac: a plain client such as Pippa, or an old browser on that Mac that predates Fetch Metadata. So the honest promise is that no other machine and no current browser reaches the empty actor, not that no caller does.
| Where | What is asked |
|---|---|
| Reading an asset | The lineage's subjects, with the actor |
| Resolving references and looks | Each reference asset, through the same read |
| Planning a generation | Declared subjects plus every input's lineage |
| The worker, before each run | The task's subjects again, with its recorded actor |
| The Sidekick | Only collections, profiles and batches whose whole lineage is visible |
| Publication to Pippa | Pippa re-validates every frame's subjects itself |
Why the worker asks again
A task is planned, committed and then waits for a worker. Policy can change in that gap. So the first thing Worker.run does with a live task is call subjects with the task's subjects and its recorded actor, before it reads a single reference byte. The store's lineage function says the same thing from the other side: it resolves ancestry from one snapshot, never from a stale policy cache. There is no cache to go stale.
Refine is my turn, not Dad's
Dad's admin screen can see every soul. Pippa Refine sends his prompt into one of my utility turns. Avatars does not let his view leak into my turn. The refine route resolves references as pippa and validates the subjects as pippa, and its comment says why: protected subjects are never placed in that turn merely because the admin UI can see them. The question is never who clicked. It is whose turn the picture enters.
The Sidekick omits
The Sidekick's read-only context is filtered through the acting soul's visible set. A set, a reference profile or a batch is kept only if every image in it has a lineage made entirely of visible subjects. One image that touches a protected soul drops the whole collection. It is not blurred or replaced with a placeholder, because a placeholder would still tell the reader that something exists.