Multi-Tenancy
NearSync supports two multi-tenancy models from a single codebase with zero code forking. Managed clients share a NearSync-hosted Supabase instance, isolated by organization ID and Row-Level Security policies. BYOK (Bring Your Own Keys) clients run the same application code against their own Supabase project for full data sovereignty.
Core Principle: One Codebase, Many Clients
Every client runs the exact same application code. There are no forks, no per-client repositories, and no industry-specific templates. The difference between clients is configuration:
+----------------------------------------------------------+
| NearSync Platform (one codebase) |
| apps/admin-dashboard apps/client-portal packages/* |
+--------------+-----------------------+-------------------+
| |
+---------v----------+ +---------v-----------+
| Managed Path | | BYOK Path |
| | | |
| NearSync Supabase | | Client's Supabase |
| (shared, multi- | | (dedicated, client- |
| tenant via org_id)| | owned infra) |
| | | |
| org_id RLS isolates| | Full DB is theirs |
| each client's data | | No seat limits |
+---------------------+ +----------------------+
The application reads a SystemManifest from the database at runtime and gates modules, features, branding, and limits accordingly. See Manifest-Driven Gating for details on how runtime configuration works.
Managed Multi-Tenancy
How It Works
All managed clients share a single Supabase project. Each client is assigned an org_id when their organization is created. Every tenant-scoped table includes an org_id column, and RESTRICTIVE Row-Level Security policies ensure that queries only return rows belonging to the authenticated user's organization.
Client User Logs In
|
v
Supabase Auth
- User created in auth.users table
- JWT issued with user_id claim
|
v
Profile Lookup (profiles table)
WHERE id = <user_id>
-> Resolves org_id, role, name, avatar
|
v
Every Query Scoped by org_id
RLS Policy (RESTRICTIVE):
org_id = (
SELECT org_id FROM profiles
WHERE id = auth.uid()
)
Applied automatically to SELECT, INSERT, UPDATE, DELETE
RLS Enforcement
NearSync uses PostgreSQL RESTRICTIVE policies for tenant isolation. The key properties of this approach:
- RESTRICTIVE policies are AND-ed with any permissive policies. Even if a permissive SELECT policy grants access, the RESTRICTIVE org_id check still applies.
- No cross-tenant data leakage is possible at the database level. A user cannot see, update, or delete rows from another organization.
- Automatic enforcement - Supabase applies RLS to all queries, including those made through the Supabase client libraries and direct SQL.
Every tenant-scoped table carries an org_id column with isolation enforced, spanning all functional domains:
| Domain | Example Tables |
|---|---|
| Core | organizations, profiles, global_settings, audit_logs |
| CRM | crm_contacts, crm_companies, crm_pipelines, crm_tasks |
| Finance | finance_invoices, finance_company_expenses, finance_payments |
| HRMS | hrms_employees, hrms_attendance, hrms_payroll |
| Support | support_tickets, support_attachments, support_comments |
| Comms | chat_messages, email_logs, whatsapp_messages |
| Projects | projects, project_tasks, project_sprints, project_documents |
| Workflows | workflow_definitions, workflow_executions, workflow_approval_requests |
| Bookings | bookings, booking_links, booking_time_slots |
| Assets | assets, asset_allocations, asset_maintenance |
| Knowledge Base | kb_articles, kb_categories, kb_ratings |
| Custom Fields | custom_field_definitions, crm_contact_custom_fields |
| Marketing | marketing_campaigns, marketing_email_sequences |
| Integrations | integration_configs, webhook_endpoints, oauth_states |
Managed Provisioning
When a new managed client signs up:
- Auth trigger creates an
organizationsrow with a neworg_id - A
SystemManifestis seeded based on the selected tier - An admin profile is created with the
super_adminrole - Default RBAC roles and permissions are seeded
- The user is redirected to the admin dashboard with all gated modules visible
No Vercel projects to create. No repositories to fork. No DNS to configure. The client uses the same deployment as every other managed client, isolated by their org_id.
Tier upgrades are handled by updating the SystemManifest in the database. Module access and feature flags change in real time with no infrastructure changes and no redeployment.
BYOK (Bring Your Own Keys)
How It Works
BYOK clients deploy the same application code but point to their own Supabase project via environment variables. The client owns their entire database, including encryption keys, backups, and region selection.
Key differences from managed:
| Aspect | Managed | BYOK |
|---|---|---|
| Database | Shared Supabase instance | Client's own Supabase project |
| Isolation mechanism | org_id + RESTRICTIVE RLS | Single-tenant; entire DB belongs to client |
| Seats | Enforced via SystemManifest max_users | No seat limit; client owns infrastructure |
| Data sovereignty | NearSync manages infrastructure | Client owns all data, encryption keys, backups |
| RLS role | Enforces org-level isolation | Enforces role-based access (admin vs. user) |
| Monitoring | Direct metadata queries on shared DB | Sentinel heartbeat via Edge Function (metadata only, no PII) |
BYOK Provisioning
During the onboarding process:
- Client creates their own Supabase project (in any supported region)
- Client provides connection credentials via the Setup tab in the client portal
- NearSync validates the connection
- The full schema (all tables with migrations) is applied to the client's database
- A
SystemManifestis seeded with the client's tier configuration and region defaults - An admin user and RBAC hierarchy are created
- A Sentinel heartbeat Edge Function is deployed for monitoring
BYOK Deployment Options
NearSync-managed hosting (default): A separate Vercel project is created pointing to the same codebase repository, configured with the client's Supabase environment variables. When NearSync pushes to main, the client's project auto-rebuilds alongside all others. Every client receives updates simultaneously.
Client self-hosted (enterprise): NearSync provides static build artifacts and deployment guides per release. The client deploys to their own Vercel, Netlify, Cloudflare Pages, or Docker infrastructure. Database migration diffs are provided with each release for the client to apply.
BYOK Key Handling
Security is a first-class concern in the BYOK flow:
- The client's
service_role_keyis used once during provisioning to apply schema and seed defaults - After provisioning completes, the key is discarded. It is never stored on NearSync infrastructure and never logged.
- The client retains sole custody of their service role key going forward
- The application code only uses the
anon_key(read-only, RLS-enforced) at runtime
Software Update Propagation
| Deployment Path | How Updates Flow |
|---|---|
| Managed | Automatic. Push to main triggers Vercel rebuild. All managed clients share the same deployment. |
| BYOK (NearSync-hosted) | Automatic. Same mechanism as managed. Push to main rebuilds the client's Vercel project too. |
| BYOK (Self-hosted) | Manual. Client pulls latest build artifacts from the release. NearSync provides migration SQL diffs per release. |
Sentinel Monitoring
NearSync's monitoring system (Sentinel) works differently for each deployment model:
Managed clients: NearSync queries metadata-only views on the shared database. These views expose aggregate statistics (active user count, recent activity counts, tier information) without accessing any PII.
BYOK clients: A Sentinel Edge Function deployed to the client's Supabase responds to authenticated heartbeat requests from NearSync. The heartbeat returns only metadata: active user count, tier, application version, and a timestamp. NearSync polls hourly. No PII is transmitted.
The Sentinel heartbeat is authenticated via signed JWT. Unauthenticated requests are rejected.
Region Defaults
When provisioning a new client, operational defaults are applied based on the client's region:
| Region | Timezone | Currency | Date Format | Default Work Days |
|---|---|---|---|---|
| AE | Asia/Dubai | AED | DD/MM/YYYY | Mon-Fri |
| US | America/New_York | USD | MM/DD/YYYY | Mon-Fri |
| EU | Europe/London | EUR | DD/MM/YYYY | Mon-Fri |
| IN | Asia/Kolkata | INR | DD/MM/YYYY | Mon-Sat |
| SA | Asia/Riyadh | SAR | DD/MM/YYYY | Sun-Thu |
These defaults are written into the SystemManifest at provisioning time and can be customized by the client afterward.
Custom Fields Engine
Instead of maintaining per-industry code forks, NearSync uses a custom fields engine to support vertical-specific requirements.
Each organization can define custom fields on any entity type (contacts, companies, deals, invoices, employees, expenses, and more). Field definitions include type (text, number, select, multi-select, date, boolean, URL, email, phone, currency), validation rules, and whether the field is required.
Field values are stored in entity-specific tables and rendered dynamically by a CustomFieldInput component that reads the definitions from the manifest.
This means a healthcare client can add fields like "medical license number" and "insurance provider" while a retail client adds "product SKU" and "inventory location", all using the same application code.