Every app with shared access eventually gets asked for "can my daughter manage my account for me." My instinct was full RBAC. What MyVitals actually needed — one person managing a household's lab reports — was closer to "let me act as this person entirely," not granular per-resource permissions. Building it safely was the interesting part.
The model: an invite is a membership, not a separate thing
FamilyMember is one row per (familyId, invitedEmail), representing a pending invite and an accepted membership as the same document transitioning state. userId stays null until the invited email matches an account.
One detail worth stealing: the invite token is cleared on acceptance via undefined, not null. There's a sparse unique index on it, and sparse indexes only exclude absent fields — null still counts as a value and collides the moment a second person accepts. One character between "works" and "second invite ever throws a duplicate key error."
The permission check is one function, and it is never cached






