Party-Level Isolation and Tenant Types Design
Table of Contents
Overview
This document defines the architecture for party-level data isolation within tenants and the revised tenant type taxonomy. Together these provide fine-grained access control: tenants isolate organisations from each other, and parties isolate business units within an organisation.
Goals
- Enforce party-level data isolation at the database level using PostgreSQL RLS.
- Define tenant types that gate available features and operational controls.
- Define party types that distinguish administrative from business entities.
- Support arbitrarily nested party hierarchies with subtree-scoped visibility.
- Decouple tenant provisioning (system operation) from party setup (business operation).
Success Criteria
- A user logged in at a leaf party cannot see data belonging to sibling or ancestor parties.
- A user logged in at an intermediate party sees their own data plus all descendant party data.
- The system party within each tenant has full visibility over all parties.
- Evaluation tenants allow relaxed operations (GLEIF onboarding, bulk import); production tenants enforce strict controls.
Tenant Types
Each tenant has a type that determines its operational characteristics, available
features, and the level of controls enforced. The type is stored in
ores_iam_tenant_types_tbl and validated on tenant creation.
| Type | Purpose | Controls |
|---|---|---|
system |
Platform administration and shared governance | Platform-managed |
production |
Real customer organisations | Strict |
evaluation |
Realistic environments for demos and testing | Relaxed |
automation |
Automated test infrastructure | None (programmatic) |
system
The system tenant (tenant 0, max UUID) is the platform-level tenant that owns shared governance data: the tenant registry, system-wide permissions, and reference datasets in the data librarian. There is exactly one system tenant per deployment. Users logged into the system tenant are super administrators with cross-tenant visibility.
production
Production tenants represent real customer organisations with isolated business operations. These tenants enforce strict operational controls:
- Four-eyes authorisation for sensitive operations (counterparty onboarding, party modifications).
- KYC-gated counterparty creation with supporting documentation.
- Data librarian operations restricted to safe, auditable actions.
- No bulk import of parties or counterparties from external datasets.
evaluation
Evaluation tenants provide realistic, production-like environments for demonstrations, user acceptance testing, QA, and system evaluation. Controls are relaxed to allow rapid environment setup:
- GLEIF-based party creation available (importing root party and child parties from LEI data).
- Bulk counterparty import from LEI datasets via the data librarian.
- No four-eyes requirement for sensitive operations.
- Clearly labelled in the UI to prevent confusion with production tenants.
Evaluation tenants are suitable for pre-production testing, sales demos, training environments, and exploratory testing by QA teams.
automation
Automation tenants are created and destroyed programmatically by test harnesses for unit, integration, and load testing. They are not intended for human interaction:
- Created with random UUIDs for isolation between test runs.
- No UI-driven workflows; all operations performed via test fixtures.
- Parallel test execution without interference.
- Ephemeral; typically torn down after test completion.
Rename from Current Types
| Current | New | Notes |
|---|---|---|
platform |
system |
Clearer intent |
organisation |
production |
Reflects operational mode, not structure |
test |
automation |
Distinguishes from human QA testing |
| (new) | evaluation |
Fills gap for demo/evaluation use |
Party Types
Each party has a type that determines its role within the tenant.
| Type | Purpose | Auto-created |
|---|---|---|
system |
Administrative party (party 0) | Yes |
operational |
Business entity (trades, books, KYC) | No |
system Party
Every tenant has exactly one system party, created automatically during tenant provisioning. The system party:
- Is the administrative home for tenant admin accounts.
- Has full visibility over all parties in the tenant (equivalent to root in the hierarchy model).
- Owns any party-scoped data that is administrative rather than business in nature.
- In tenant 0, it is the only party and serves as the platform admin party.
The system party is analogous to the system tenant: it is an infrastructure construct, not a business entity.
operational Party
Operational parties represent real business entities: legal entities, branches, desks, subsidiaries. They:
- Are created by users during normal business operations.
- Form an arbitrarily nested hierarchy via
parent_party_id. - Have tree-scoped visibility: a party can see its own data and all descendant party data.
- Own business data: counterparties, books, portfolios, trades.
The root operational party (parent_party_id = NULL among operational parties,
or parent_party_id pointing to the system party) represents the top of the
business hierarchy, typically the holding company or group entity.
Data Ownership Patterns
Data within a tenant falls into two categories based on ownership scope.
Tenant-Scoped Data (Shared Reference Data)
Reference data defined once at the tenant level, sourced from standards bodies (ISO, FpML). All parties within the tenant share a common definition.
| Entity | Source | Party Relationship |
|---|---|---|
| Currencies | ISO 4217 | Visibility scoped via junction |
| Countries | ISO 3166-1 | Visibility scoped via junction |
| Business centres | ORE | Visibility scoped via junction |
Party-level visibility is controlled through junction tables (e.g.
party_currencies) that define which currencies a given party can see and use.
The underlying currency definitions remain shared. This is a future enhancement.
Party-Scoped Data (Isolated Operational Data)
Operational data owned by a specific party. Each party maintains its own records independently. Even when two parties deal with the same external legal entity, they each have separate records (different KYC status, credit limits, documentation).
| Entity | Isolation Mechanism | Notes |
|---|---|---|
| Counterparties | party_id column + RLS |
Own KYC, own records |
| Books | party_id column + RLS |
Trading books |
| Portfolios | party_id column + RLS |
Position aggregations |
| Trades | party_id column + RLS |
Individual transactions |
Party-scoped tables have a party_id column and RLS policies that filter based
on the session's visible party set.
Party-Level RLS Architecture
Party isolation follows the same pattern as tenant isolation but adds a second layer of RLS filtering.
Session Variables
At login, three values are set on the database session:
app.current_tenant_id = '<tenant-uuid>' -- existing
app.current_party_id = '<party-uuid>' -- new
app.visible_party_ids = '{uuid1,uuid2,...}' -- new
Visible Party Set Computation
At login, after the user selects their party, the server computes the full set of visible party IDs using a recursive CTE:
WITH RECURSIVE party_tree AS ( -- Start with the user's party SELECT id FROM ores_refdata_parties_tbl WHERE id = $user_party_id AND tenant_id = $tenant_id AND valid_to = ores_utility_infinity_timestamp_fn() UNION ALL -- Add all descendants SELECT p.id FROM ores_refdata_parties_tbl p JOIN party_tree pt ON p.parent_party_id = pt.id WHERE p.tenant_id = $tenant_id AND p.valid_to = ores_utility_infinity_timestamp_fn() ) SELECT array_agg(id) FROM party_tree;
For the system party, the CTE returns all parties in the tenant, giving full visibility.
The visible set is computed once at login and is immutable for the session lifetime. If the party tree changes mid-session, the user must re-login to pick up new visibility.
RLS Policy
Party-scoped tables use a single, simple RLS policy:
CREATE POLICY party_isolation ON ores_refdata_counterparties_tbl USING ( party_id = ANY(current_setting('app.visible_party_ids')::uuid[]) );
This covers all cases:
- System party user: visible set = all parties. Sees everything.
- Root operational party user: visible set = root + all descendants.
- Mid-level party user: visible set = their party + their subtree.
- Leaf party user: visible set = just their party.
Performance
| Operation | Cost |
|---|---|
| Recursive CTE at login | Once per login (~1ms typical) |
current_setting() in RLS policy |
Constant per query plan |
ANY(uuid[]) membership check |
O(n) where n = visible party count |
For realistic hierarchies (up to a few hundred parties per tenant), the UUID array stays well within PostgreSQL session variable limits and the membership check is fast.
Login Flow Changes
Current Flow
- Client sends
login_requestwithusername@hostname. - Server resolves tenant from hostname.
- Server authenticates user, creates session bound to tenant.
- Client proceeds.
Proposed Flow
- Client sends
login_requestwithusername@hostname. - Server resolves tenant from hostname.
- Server authenticates user.
- Server looks up user's parties from
account_parties. - If one party: auto-select. If multiple: return party list to client.
- Client displays party picker (if needed) and sends party selection.
- Server computes
visible_party_idsvia recursive CTE. - Session is created with tenant ID + party ID + visible party set.
- Client proceeds. Current party is displayed in the application window.
UI Considerations for Multi-Party Users
When a user's visible set spans multiple parties:
- Party-scoped table views gain a "Party" column showing the owning party.
- A party filter dropdown allows scoping the view to a specific subtree.
- Single-party users see the party column hidden (always the same value).
Tenant Provisioning
Tenant creation is a system-level operation. It creates the minimal infrastructure for a tenant to operate.
Provisioning Creates
- Tenant record in
ores_iam_tenants_tbl. - System party (party 0) for the new tenant.
- Copies of system-level permissions and roles from the system tenant.
- One or more admin accounts (associated with the system party).
Provisioning Does NOT Create
- Business parties (the "house"). This is a separate business operation.
- Reference data (currencies, countries). Published via the data librarian.
- Counterparties. Created per-party through appropriate workflows.
This separation ensures that tenant creation is a clean, predictable system operation, and business setup is done by the people who understand the business.
Evaluation Tenant Variant
For evaluation tenants, an additional step is available after provisioning:
importing operating parties from GLEIF/LEI data. This is a separate action,
not part of provisioning itself, and is gated by the evaluation tenant type.
Single-Tenant Mode (Future)
The design must not preclude a deployment where tenant 0 (the system tenant) is the only tenant. In this mode:
- The system party of tenant 0 serves as the administrative party.
- Users create operational parties directly within tenant 0.
- All data lives in a single tenant.
This is useful for small deployments or single-organisation setups. It does not require special implementation now; it simply means we must avoid assumptions that tenant 0 cannot have operational parties.
Open Questions
None at this time. The following items are deferred to separate stories:
- Four-eyes authorisation framework.
- KYC workflow for counterparty onboarding in production tenants.
- Data librarian lockdown for production tenants.
- GLEIF-based evaluation onboarding wizard.
- Currency/country party-visibility junction tables.
- Self-registration and approval workflow for production tenants.
- Single-tenant mode implementation.
- Party selection protocol changes (login flow extension).
See also
- System Bootstrapping Concepts and Provisioning — user-facing explanation of tenants, tenant types, parties, party hierarchy, and the provisioning wizards (System Provisioner, Tenant Provisioner, Party Provisioner).
- ORE Studio Manuals — index of all user and developer guides.