Skip to content
Docs

Command palette

Jump to a page or view, or find a todo by name.

Changelog

Organizations are the tenant

The repository's architectural decision, rationale and consequences.

Markdown

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.

Edit on GitHubDocumentation is in English.