The template is a SaaS starter on one Neon database. Several people need to share todos and files without seeing each other's work.
We use Better Auth's organization plugin (organization / member / invitation + session.activeOrganizationId) instead of a custom tenant table. Shared schema, row-level organizationId. Teams and dynamic org roles stay off.
We already generated these tables once and dropped them as unused (20260825200958). This time the plugin is wired, domain tables are scoped, and signup creates a personal Organization.
Platform admin (user.role) stays global. Org roles do not grant /admin.
Files live on Cloudflare R2. The Worker credential is one bucket
(STORAGE_BUCKET). Isolation is the server-enforced prefix
orgs/<organizationId>/ plus a membership check (getActiveMember reads
member; the 5-minute cookie cache is not authorization). The client cannot
supply or escape the prefix. Never fall back to users/<userId>/.
Minting a bucket per Organization would 500 with a bucket-scoped R2 token.
Neon stays one shared database with row-level organizationId.