Files
as4-411/docs/architecture/tenant-model.md
defiQUG c24ae925cf
Some checks failed
CI / lint (push) Has been cancelled
CI / build (push) Has been cancelled
Initial commit: AS4/411 directory and discovery service for Sankofa Marketplace
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 08:44:20 -08:00

33 lines
2.3 KiB
Markdown

# Tenant Model and Row-Level Security
## Global vs Tenant-Private
- **Global objects:** Public or shared data that can be read across tenants (e.g. BIC, LEI, BIN range metadata). Stored with `tenant_id` null or a dedicated "global" tenant. Used for cross-tenant lookup when the rail has a public identifier scheme.
- **Tenant-private objects:** Participant-specific data, merchant IDs, terminal IDs, contractual endpoints. Always scoped by `tenant_id`. Queries must supply tenant (from auth or request) so that only that tenant's rows are visible.
## Enforcement
- **Postgres Row Level Security (RLS):** Enable RLS on `participants`, `identifiers`, `endpoints`, `capabilities`, `credentials`, `policies`, and optionally `routing_artifacts`. Policies: `tenant_id = current_setting('app.current_tenant_id')` or equivalent. Global rows: allow when `tenant_id IS NULL` or when reading public identifiers.
- **Application layer:** Resolver and Admin API must set tenant context (e.g. from JWT or request parameter) before querying. Never return rows from another tenant.
- **Per-tenant encryption:** For Tier 2+ data (see [data-classification](../security/data-classification.md)), use per-tenant encryption keys so that key compromise affects only one tenant.
## Caching
- Cache key **includes tenant**: same request for different tenants must not share a cache entry.
- Optional per-tenant TTL or invalidation rules (e.g. shorter TTL for high-churn tenants).
- Negative cache: key includes tenant; invalidate on any change for that tenant.
## RLS Policy Summary
| Table | Policy (conceptual) |
| ---------------- | -------------------------------------------------------- |
| participants | WHERE tenant_id = current_tenant OR tenant_id IS NULL |
| identifiers | JOIN participants; same tenant or global |
| endpoints | JOIN participants; same tenant |
| capabilities | JOIN participants; same tenant |
| credentials | JOIN participants; same tenant |
| policies | WHERE tenant_id = current_tenant |
| routing_artifacts| WHERE tenant_id = current_tenant OR tenant_id IS NULL |
Apply `current_tenant` from connection/session (e.g. set by API after auth).