> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avyratech.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Tenancy & RLS

> How Tonnex handles multi-tenancy using Row Level Security.

# Multi-Tenancy Architecture

Tonnex uses a **Table-Based Multi-Tenancy** approach with **Row Level Security (RLS)**.

Every tenant-specific table has an `org_id` column that links to the `organizations` table.
Access is enforced at the database level using Postgres RLS policies.

## Key Components

### 1. Organization Membership Check

We use a `SECURITY DEFINER` function to verify if a user belongs to an organization.
This bypasses RLS on the `org_memberships` table itself to avoid infinite recursion.

```sql theme={null}
CREATE OR REPLACE FUNCTION public.is_org_member(_org_id uuid)
RETURNS boolean AS $$
BEGIN
  RETURN EXISTS (
    SELECT 1 FROM public.org_memberships 
    WHERE user_id = auth.uid() 
      AND org_id = _org_id
  );
END;
$$ LANGUAGE plpgsql SECURITY DEFINER STABLE;
```

### 2. Drizzle Integration (Supabase)

We use `drizzle-orm/supabase` helpers to define RLS policies directly in the schema.

```typescript theme={null}
import { pgPolicy } from "drizzle-orm/pg-core";
import { authenticatedRole, isOrgMember } from "./rls";

export const projects = pgTable("projects", {
  // ... columns
  orgId: uuid("org_id").references(() => organizations.id),
}, (table) => [
  // Policy: Users can only see projects in their organizations
  pgPolicy("view_projects", {
    for: "select",
    to: authenticatedRole,
    using: isOrgMember(table.orgId),
  }),
]);
```

### 3. RLS-Aware Client

When querying from the application (Server Actions/Components), we use a wrapper that sets the RLS context for the transaction.

```typescript theme={null}
import { createDrizzleSupabaseClient } from "@tonnex/db";

// In a Server Action
const db = createDrizzleSupabaseClient(accessToken);
const data = await db.rls((tx) => tx.select().from(projects));
```

This sets:

* `auth.uid()` via `request.jwt.claim.sub`
* `auth.jwt()` via `request.jwt.claims`
* `role` via `set local role`

## Adding a New Module

When creating a new module (e.g., `vehicles`):

1. Add `orgId` to your table schema.
2. Add the RLS policy usage `isOrgMember(table.orgId)` for SELECT/UPDATE/DELETE.
3. Use the `createDrizzleSupabaseClient` wrapper for all queries.
