Torn implements a schema-per-tenant architecture using PostgreSQL’s native schema isolation. Each company (tenant) gets its own dedicated database schema, providing complete data isolation while sharing the same database instance.
This approach combines the security benefits of separate databases with the operational simplicity of a single PostgreSQL instance.
The tenant_users table manages which users can access which tenants:
app/models/saas.py
class TenantUser(Base): """Tabla intermedia: Usuario Global <-> Tenant. Define a qué empresa tiene acceso un Usuario Global y con qué rol. """ __tablename__ = "tenant_users" __table_args__ = {'schema': 'public'} id = Column(Integer, primary_key=True) tenant_id = Column(Integer, ForeignKey("public.tenants.id")) user_id = Column(Integer, ForeignKey("public.saas_users.id")) role_name = Column(String(50), default="user") is_active = Column(Boolean, default=True)
When a new company signs up, Torn executes a multi-stage provisioning workflow:
1
Generate Schema Name
Creates a unique schema identifier based on the company’s RUT:
app/services/tenant_service.py
def _generate_schema_name(rut: str) -> str: """Genera un nombre de esquema seguro basado en el RUT.""" clean_rut = re.sub(r'[^a-zA-Z0-9]', '', rut.lower()) return f"tenant_{clean_rut}"
Torn uses SQLAlchemy’s schema_translate_map to dynamically route queries to the correct tenant schema:
app/dependencies/tenant.py
def get_tenant_db(tenant_user: TenantUser = Depends(get_current_tenant_user)): """Returns a DB session bound to the tenant's schema.""" schema_name = tenant_user.tenant.schema_name db = SessionLocal() db.connection( execution_options={"schema_translate_map": {None: schema_name}} ) try: yield db finally: db.close()
All tenant-specific operations use get_tenant_db() as a dependency, ensuring queries are automatically scoped to the correct schema without manual prefixing.
Each tenant’s data exists in a separate PostgreSQL schema. Even with SQL injection vulnerabilities, one tenant cannot access another’s data without explicit schema switching—which requires authentication.
Backup Granularity
You can backup or restore individual tenant schemas using:
pg_dump -n tenant_12345678k torn_db > backup.sql
Migration Safety
Schema-level migrations can be tested on a single tenant before rolling out globally. Failed migrations only affect one customer.
Performance Isolation
Heavy queries from one tenant don’t lock tables for others, as each schema maintains separate table instances.