Identity & Ownership
How Person, Group, and owner_id form the foundation of multi-tenancy
Person vs Group
The Identity kernel contains two core entities:
An individual user. Has authentication credentials, personal settings, and owns private data.
A collection of people who share data. Could be a family, team, or organization. Members have roles.
Every component that stores data references either a Person or Group—never both, and never neither. This is the owner_id pattern.
The owner_id Pattern
Every entity carries an owner_id: a UUID pointing to either a Person or Group in the Identity kernel. This single field enables:
- Multi-tenancy — Data is scoped by owner; no leakage between users
- Sharing — Same entity structure works for private (Person) and shared (Group) data
- Querying — Every repository can filter by owner_id
@dataclass class Task: id: UUID owner_id: UUID # Person or Group title: str status: TaskStatus container_id: UUID # Which list/project ...
A task owned by a Person is private. The same task structure owned by a Group becomes shared—visible to all group members. The Todos component doesn't distinguish; it just filters by owner_id.
Multi-Tenancy Without Complexity
Components don't know whether they're serving a Person or Group. They just:
- Receive
owner_idin API requests - Store it on every entity they create
- Filter by it on every query
Authorization (can this user access this owner_id?) is handled by Policy, not by individual components.
owner_id: UUID"] PLAN["BudgetPlan
owner_id: UUID"] TASK -.->|owner_id| PERSON TASK -.->|owner_id| GROUP PLAN -.->|owner_id| PERSON PLAN -.->|owner_id| GROUP style PERSON fill:#f3e8ff,stroke:#9333ea style GROUP fill:#f3e8ff,stroke:#9333ea style TASK fill:#ecfeff,stroke:#0891b2 style PLAN fill:#fef3c7,stroke:#d97706
- All entities MUST have
owner_id owner_idis the query scope boundary—always filter by it- Never expose entities across owner boundaries without explicit sharing
- The Identity kernel owns Person and Group; components only store references
- Don't treat owner_id as auth — Use Policy to check if the current user can access the owner_id
- Don't branch on Person vs Group — Your component shouldn't care which type it is
- Don't store Person/Group details — Only store the UUID; fetch details from Identity if needed