This page describes the multi-tenant Organizations model introduced in
CYPEX v2.0.0 and why PostgreSQL Row-Level Security (RLS) is part of it.
The single-tenant model (v1.x)
Before v2.0.0, a CYPEX instance was effectively single-tenant:
One database, one application schema, one set of users.
Multi-tenancy was emulated at the application layer by filtering rows on a
client identifier.
PostgreSQL row-level security was not enabled on tenant-scoped tables.
This worked, but every enforcement decision lived in application code. A
single missing WHERE clause, an ad-hoc reporting query, or a hand-written
maintenance script could leak rows across tenants.
Before you start assigning clients to organizations, walk through this
preparation checklist. The procedures on this page are the same shape as
the v2.0.0 pre-upgrade checklist, but
focused on the post-upgrade multi-tenant setup rather than the upgrade
itself.
1. Confirm the v2.0.0 upgrade is complete
The procedures here assume the v2.0.0 migrations have been applied. Verify:
This page is the mandatory upgrade entry point for CYPEX v2.0.0. It is
written for operators who run CYPEX in production
and need a single walkthrough that covers every breaking change in this
release.
Audience. Read this end-to-end before opening the maintenance window.
It links out to detailed pages for each topic; follow every link in
the order presented.
What is changing in v2.0.0
v2.0.0 introduces seven platform-level changes. Each one is breaking or
behavior-changing for at least one class of deployment.
CYPEX v2.0.0 introduces multi-tenant Organizations with PostgreSQL
Row-Level Security (RLS). Tenant isolation is enforced in the
database, not by application-layer filters that can be bypassed or
misconfigured. This is the most significant schema and permission-model
change since v1.0.0.
This section is the canonical place to plan, validate, and execute a v2.0.0
upgrade. It is written from the perspective of factual upgrade behavior:
what the migrations actually do, what is additive, what is breaking, and
what is behavior-changing.
This page lists the changes in CYPEX v2.0.0 that may require action on
the part of operators, integrators, or end users. These are the changes you
need to plan for during the upgrade.
1. cypex_user becomes SELECT-only on most tables
The cypex_user role loses INSERT, UPDATE, and DELETE privileges on
most tenant-scoped tables. After the upgrade, cypex_user is essentially
read-only on those tables, with the exceptions listed below.
This page walks through the SQL primitives for enabling PostgreSQL
Row-Level Security on a CYPEX application-schema table and attaching the
baseline organization policy.
The v2.0.0 migration already enabled RLS on CYPEX core tables
(t_object, t_object_field, t_ui, etc.). This page is about enabling
RLS on client application-schema tables during the post-upgrade
multi-tenant setup.
Prefer the automated
client-add-organization-id SQL template
for bulk migrations. Use the primitives below when you need a single-table
or large-table (batched) path.
This page explains how to use the
client-add-organization-id SQL template
to backfill the organization_id column on existing application-schema
tables and assign clients to organizations.
Warning
Rehearse this on a staging copy before running it in production. This
migration has failed in the field: the failure modes and the safeguards that
prevent them are listed under Failure modes below.
This page lists the changes in CYPEX v2.0.0 that alter the default
behavior of the platform without breaking existing integrations. You
should be aware of these changes, but they typically do not require
action.
1. Default password policy now enforced
A new row is inserted into cypex.t_config with key password_policy and
value:
This page lists the purely additive changes in CYPEX v2.0.0. These do
not modify the behavior of existing functionality — they add new tables,
functions, indexes, and roles that integrate with the Organizations model.
1. New tables
cypex.t_organization
The central tenant table. Created by the v2.0.0 upgrade with one row already
inserted: the Default Organization (organization_domain = 'default').