---
title: "Multi-Tenancy"
description: "Every agent-native app is multi-tenant out of the box — organizations, team members, roles, and per-org data isolation, with zero configuration."
---

# Multi-Tenancy

Every Agent-Native app includes organizations and team membership. The
framework provides access helpers and a SQL scoping layer for tenant data.
Applications still need to declare ownable tables and use the documented
server-side access patterns.

## What you get for free {#free}

A fresh `npx --yes @agent-native/core@latest create` scaffold already ships with:

- **User registration and login** — see [Authentication](/docs/authentication).
- **Organizations** — users create orgs and invite members by email. Each org is a fully isolated tenant.
- **Roles** — every member is an `owner`, `admin`, or `member`; actions can check the role for authorization.
- **Org switching** — the session tracks the active org (`session.orgId`), and switching it changes the data the user and agent see.
- **Per-org data isolation:** framework access helpers and agent SQL scoping
  enforce the active organization boundary when used as documented.

If you're evaluating agent-native for a CRM, project tracker, support inbox, or any team tool, the multi-tenant foundation is already there. All first-party templates are multi-tenant — see [Templates](/docs/cloneable-saas) for the list.

<Diagram id="doc-block-1nlyz0h" title="Org membership and isolation" summary={"Users join organizations as owner/admin/member. Every ownable row carries the org_id of the tenant that owns it, and no row leaks across the boundary."}>

```html
<div class="mt-grid">
  <div class="diagram-card">
    <span class="diagram-pill accent">Org A</span
    ><small class="diagram-muted">members: alice (owner), bob (member)</small>
    <div class="diagram-box">rows where org_id = A</div>
  </div>
  <div class="diagram-card">
    <span class="diagram-pill accent">Org B</span
    ><small class="diagram-muted">members: carol (owner)</small>
    <div class="diagram-box">rows where org_id = B</div>
  </div>
</div>
<div class="mt-wall" aria-hidden="true">
  <span class="diagram-pill warn">no cross-org reads</span>
</div>
```

```css
.mt-grid {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}
.mt-grid .diagram-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  flex: 1;
  min-width: 200px;
}
.mt-wall {
  display: flex;
  justify-content: center;
  margin-top: 12px;
}
```

</Diagram>

## The account menu {#org-switcher}

The account menu (`OrgSwitcher`, at the bottom of the sidebar) and the organization pages in Settings render in every template with no extra code. They drive the core org REST routes under `/_agent-native/org/*` (create org, switch org, list/invite/remove members, change roles, set allowed email domain). Users pick the active org from the account menu; **Settings › Organization › Members** handles invitations and role changes.

This is the framework's own `org/` module, not Better Auth's organization
plugin. The full org-management surface, its routes, and template-authored
`defineAction` wrappers are documented in
[Authentication → Organizations](/docs/authentication#organizations).

## How isolation works {#isolation}

Tenant data commonly uses an `org_id` column from `ownableColumns()`. The
request context carries the active org. Server reads and writes should use
`accessFilter()` or `assertAccess()`. Agent SQL receives temporary filtered
views for ownable tables. The `no-unscoped-queries` check runs as part of
`pnpm guards` and is also available through the doctor. These layers do not
replace application authorization checks.

<Diagram id="doc-block-15rq3ee" title="Organization context and data access" summary="The active org is part of request context. Server access helpers and agent SQL scoping enforce the declared data boundary.">

```html
<div class="mt-pipe">
  <div class="diagram-node">
    session.orgId<br /><small class="diagram-muted"
      >active org on session</small
    >
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-node">
    AGENT_ORG_ID<br /><small class="diagram-muted">request context</small>
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-box">
    Scoped data access<br /><small class="diagram-muted"
      >server helpers and agent SQL views</small
    >
  </div>
</div>
```

```css
.mt-pipe {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.mt-pipe .diagram-node {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 14px;
}
.mt-pipe .diagram-arrow {
  font-size: 22px;
  line-height: 1;
}
```

</Diagram>

This is the same pipeline used for per-user scoping. PostgreSQL row-level
security (RLS) is not currently part of the framework enforcement path. It is
planned as post-1.0 hardening. For the SQL-level mechanics, the
`ownableColumns()` contract, and the `accessFilter()` / `resolveAccess()` /
`assertAccess()` helpers, see [Security → Data Scoping](/docs/security#data-scoping).

## What's next {#related}

- [**Organizations, Teams & Permissions**](/docs/organizations-teams-permissions) — a practical, start-here guide to org roles, resource roles, and the environment variables involved
- [**Authentication**](/docs/authentication#organizations) — sessions, social providers, and the org-management surface
- [**Security — Data Scoping**](/docs/security#data-scoping) — SQL-level isolation, the `ownableColumns()` contract, and access guards
- [**Multi-App Workspace**](/docs/multi-app-workspace) — hosting multiple agent-native apps in one monorepo with shared auth and RBAC
