UpdaterAgent is a multi-tenant system where each tenant’s data is completely isolated from every other tenant’s. Isolation is enforced automatically at the database query level through EF Core global query filters — no manual filtering is required in application code. Every entity that participates in tenancy implements theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ShohjahonSohibov/repo-for-agent/llms.txt
Use this file to discover all available pages before exploring further.
ITenantEntity interface, which is the hook that activates automatic isolation.
How automatic query filtering works
AppDbContext.OnModelCreating registers a global query filter for every entity that implements ITenantEntity:
CurrentTenantId and IsSuperAdmin are properties on AppDbContext itself, resolved from the JWT tenantId claim via ITenantContextService on each request scope. Because these are instance properties on the DbContext (not captured constants), EF Core correctly re-evaluates them per query.
TenantContextMiddleware reads the tenant-id claim from the JWT and populates ITenantContextService at the start of every request. The DbContext properties delegate to this service.The golden rule
Never write.Where(x => x.TenantId == ...) manually. The global filter already applies this condition to every query. Adding a manual filter is redundant at best and can mask bugs where the filter is accidentally bypassed.
Automatic TenantId on insert
When an entity inAdded state is saved, AppDbContext.SaveChangesAsync calls TrackTenantId(), which:
- Finds all
Addedentities implementingITenantEntitywhereTenantId == 0 - Auto-assigns
CurrentTenantIdfrom the JWT claim - Skips entities where
TenantIdis already set (background jobs, onboarding flows)
TenantId assignment on new entities.
When to use IgnoreQueryFilters()
IgnoreQueryFilters() bypasses both the tenant filter and the soft-delete filter. It is permitted in exactly two situations:
Login — no JWT available yet
Login — no JWT available yet
At login time there is no access token, so Apply the same pattern to any query that runs before authentication: password reset, registration, and similar pre-auth flows.
CurrentTenantId is 0. The tenant filter would match no real users (TenantId > 0), causing every valid login attempt to return “invalid credentials”.Cross-tenant admin operations
Cross-tenant admin operations
System administrators need to query across all tenants. After calling
IgnoreQueryFilters(), the service must check IsSuperAdmin before proceeding and must manually re-apply the soft-delete filter.Background jobs
Background jobs run outside the HTTP request pipeline, so there is no JWT and noCurrentTenantId. TrackTenantId() skips auto-assignment when HasTenantContext is false.
Background jobs must set TenantId explicitly before querying or inserting tenant-scoped data:
tenantId explicitly to any helper that creates tenant-scoped entities.
Tables excluded from tenant isolation
The following 13 tables are intentionally excluded from the global query filter because they are shared lookup or junction tables:| Table | Reason |
|---|---|
tenants | The tenant registry itself |
permissions | Shared permission definitions |
permission_role | Junction table |
permission_tenant | Junction table |
company_user | Junction table |
label_tickets | Junction table |
countries | Global reference data |
states | Global reference data |
setting_categories | Global reference data |
addresses | Shared address records |
ai_description_histories | Cross-tenant AI logs |
refresh_tokens | Auth tokens (user-scoped, not tenant-scoped) |
user_sessions | Auth sessions (user-scoped, not tenant-scoped) |