RBAC & Tenancy
QueryWise is multi-tenant by design. This page covers the access control and isolation primitives.
Roles
Four built-in roles, in decreasing privilege:
- owner — billing, team management, full operational control. Can transfer ownership.
- admin — full operational control. Cannot manage billing or transfer ownership.
- editor — connect cloud accounts, apply recommendations, edit budgets, push tickets.
- viewer — read-only.
Role assignments are per-user, per-tenant. A user can be a member of multiple tenants with different roles in each.
Authentication
Primary: Auth0 with RS256-signed JWTs. Supported identity providers:
- Email + password
- Microsoft
- SAML SSO (enterprise)
- OIDC (enterprise)
The dev environment supports a bypass-login mode for local testing. It's gated by ENVIRONMENT=development and is never available in staging or production.
JWT validation happens in the auth middleware on every request. Tokens are validated against Auth0's JWKS endpoint with key caching. Expired tokens return 401 immediately.
MFA / SSO
MFA can be enforced per-tenant via Auth0. Two paths:
- Tenant-level enforcement — admin enables MFA, all members must enroll within a grace period.
- User-level opt-in — users can enable MFA from Settings → Security.
For enterprise plans, SAML SSO and OIDC SSO can be configured through the Auth0 management console. The tenant.settings["sso"] JSONB field stores tenant-level SSO requirements.
API keys
API keys are an alternative to user JWT for programmatic access. Properties:
- Scoped to a tenant and a role (typically
editororviewer) - Generated at Settings → API Keys, displayed once at creation
- Stored as Argon2id hashes; the plaintext is never recoverable
- Can be rotated or revoked at any time
- Optional expiration date
API key requests use the same auth middleware as JWT — the validation path differs but the downstream RBAC is identical.
Multi-tenant isolation
QueryWise uses PostgreSQL schema-per-tenant isolation:
- Shared schema
qw_sharedholds:organizations,tenants,users,audit_logs, time-series data withtenant_idpartitioning, and other cross-tenant tables. - One schema per tenant (
qw_tenant_<uuid>) holds tenant-specific objects:cloud_accounts,recommendations,budgets,cost_centers,tag_policies, etc.
The auth middleware sets the PostgreSQL search_path on the SQL session to the tenant's schema. Every downstream query is automatically scoped — there is no application-level tenant filter to forget.
Time-series data (billing_records, query_stats, usage_metrics, query_plans) lives in the shared schema with a tenant_id column and TimescaleDB hypertable partitioning. Row-level security (RLS) policies enforce tenant isolation on these tables.
Audit logging
Every API write goes through the audit middleware, which logs:
- Actor (user or API key)
- Tenant
- Action (e.g.
recommendation.apply,cloud_account.create) - Target (entity type + id)
- Request payload (sanitized — credentials and secrets are redacted)
- Outcome (success / error)
- Timestamp
Reads are sampled to keep volume manageable. The audit feed is available via:
- Activity Log dashboard page
GET /api/v1/audit/eventsAPI- Bulk CSV export for compliance reporting
Audit logs are retained for 1 year on standard plans, 7 years on enterprise.
Credential vault
Cloud account credentials are stored in the credential vault — CredentialStore abstraction with two backends:
- Fernet (default) — symmetric encryption with key managed in environment
- AWS Secrets Manager — production deployments
Credentials are never logged, never returned in API responses, and never persisted in plaintext. Tasks fetch credentials at run time and pass them only to the connector that needs them.
JIRA / ServiceNow OAuth tokens use a separate _encryption.py Fernet shim because they need different rotation semantics.
Sessions
Sessions are JWT-based with a 24-hour default lifetime (configurable per tenant). Refresh tokens are stored in HttpOnly Secure cookies.
Settings → Security lets users:
- View active sessions across devices
- Revoke individual sessions
- See last login time and IP
Admins can also revoke sessions for any user in their tenant.
Data retention
Default retention:
- Time-series data: 13 months (configurable)
- Recommendations: indefinite, but archived after apply
- Audit logs: 1 year (standard) / 7 years (enterprise)
- Query plans: 90 days
Retention is enforced by the data_retention.py worker, which runs daily.
Compliance
QueryWise is on a SOC 2 Type II readiness path. The current state:
- Tenant isolation tested and audited (
docs/security-audit-tenant-isolation.md) - Encryption at rest (database) and in transit (TLS everywhere)
- Audit logging on every write
- Quarterly access reviews (built into the platform)
For enterprise customers under formal compliance review, the SOC 2 readiness assessment is available on request.
Where to next
- Workflows & Integrations — what your team can do once roles are right.
- Cloud Connectors — credentials are scoped to roles too.