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

A Soul You May Not See

~14 min · authorization, super-soul, server-side, policy

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

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.

WhereWhat is asked
Reading an assetThe lineage's subjects, with the actor
Resolving references and looksEach reference asset, through the same read
Planning a generationDeclared subjects plus every input's lineage
The worker, before each runThe task's subjects again, with its recorded actor
The SidekickOnly collections, profiles and batches whose whole lineage is visible
Publication to PippaPippa 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.

Principle: Keep the policy in one home and make every door ask it, on every request. A rule copied into each door drifts; a rule asked at each door cannot.

Code

cwkPippa, backend/services/avatar_studio.py: validate_subjects, the whole visibility verdict (verbatim, lines 73-81)·python
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]
cwkAvatars, backend/studio.py: Studio.asset, lineage first, then policy, then the archive flag (verbatim, lines 93-98)·python
    async def asset(self, id, actor=None):
        asset = await self.io("asset_lineage", id)
        await self.subjects(asset["subjects"], actor)
        if asset.get("archived"):
            raise StudioError("This image is archived. Restore it before use.")
        return asset
cwkAvatars, backend/app.py: the refine route validates as Pippa, whoever clicked (verbatim, lines 470-476)·python
    async def refine(body: Refine):
        refs, subjects, rules = await studio.resolve_references(body.subjects, body.references, "pippa")
        # Pippa Refine is a Pippa turn: protected subjects are never placed
        # in that turn merely because the admin UI can see them.
        await studio.subjects(subjects, "pippa")
        context = body.context + ("\nIdentity rules Avatars appends automatically: " + "; ".join(rules) if rules else "")
        return await upstream.refine(body.prompt, body.workflow, context)

External links

Exercise

Pick a service you run that stores something only some callers may see. Write one function that answers, for a list of object ids and an optional acting identity, either the objects or a permission error. Then find every route that returns one of those objects and make each one call that function on every request, including any background job that picks the objects up later. Finish with a test where the policy flips between planning a job and running it, and the job refuses.
Hint
Search your code for every place that turns an id into bytes or a URL; that list is your door list. Make the acting identity a required parameter on internal routes, so forgetting it fails loudly instead of falling through to an all-seeing default.

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.