Introduces Family as the tenant boundary so recipes and cookbooks can be
scoped per household instead of every user seeing everything. Adds a
centralized access filter, an invite/membership UI, a first-login prompt
to create a family, and locks down the previously unauthenticated backup
routes to admin only.
- Family and FamilyMember models with OWNER/MEMBER roles; familyId on
Recipe and Cookbook (ON DELETE SET NULL so deleting a family orphans
content rather than destroying it).
- access.service.ts composes a single WhereInput covering owner, family,
PUBLIC visibility, and direct share; admins short-circuit to full
access.
- recipes/cookbooks routes now require auth, strip client-supplied
userId/familyId on create, and gate mutations with canMutate checks.
Auto-filter helpers scoped to the same family to prevent cross-tenant
leakage via shared tag names.
- families.routes.ts exposes list/create/get/rename/delete plus
add/remove member, with last-owner protection on removal.
- FamilyGate component blocks the authenticated UI with a modal if the
user has zero memberships, prompting them to create their first
family; Family page provides ongoing management.
- backup.routes.ts now requires admin; it had no auth at all before.
- Bumps version to 2026.04.008 and documents the monotonic PPP counter
in CLAUDE.md.
Migration SQL is generated locally but not tracked (per existing
.gitignore); apply 20260416010000_add_family_tenant to prod during
deploy. Run backfill-family-tenant.ts once post-migration to assign
existing content to a default owner's family.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>