A shared device can hold several accounts from the same server and switch
between them in a couple of taps. A profile can be locked behind a 4-8
digit PIN; one without a PIN is one tap away. Forgetting a PIN falls
through to the account's own Jellyfin password, so there is no reset flow
and no recovery secret to store.
Opt-in by construction: a single account with no PIN starts, plays and
downloads exactly as before, and never sees a picker.
Two decisions worth keeping:
- Switching is not logging out. auth_logout invalidates the token
server-side, which is precisely what a switch must not do, or every
switch back would cost a password. The switch runs as a plan
(profiles/switch.rs) so the teardown *ordering* is unit-testable with
no player and no server -- a straggler reporting after the active user
flips would attribute one account's viewing to another, silently.
- The PIN gates switching, not the token at rest. Wrapping each token
with its PIN would leave a locked profile unable to resume its own
downloads or drain its own sync queue until somebody typed the code,
which on a device that reboots nightly costs more than it defends
against a four-digit secret. auth_initialize does refuse to restore a
PIN-protected session, so the gate is on the session rather than on
which screen is shown.
"Child account" is not modelled anywhere -- a child's profile is simply
one with no PIN. The frontend renders an opaque unlockMethod and never
compares a PIN, counts an attempt or infers a role.
Migration 024 adds user_pins, user_item_visibility, user_libraries and
download_grants, and backfills the existing user so an upgrade does not
blank its library. The visibility and grant tables are the schema half of
the cache-scoping and shared-download work; the read-path enforcement is
still to come (see docs/specs/multi-user-profiles.md).