Security
Authentication, encryption, and tenant isolation
GNETiX enforces security at every layer -- from authentication and encryption to strict tenant isolation in the database.
Authentication
JWT Tokens
All API requests (except login, registration, and health checks) require a valid JWT access token in the Authorization: Bearer header.
| Token Type | Lifetime | Purpose |
|---|---|---|
| Access token | 15 minutes | Authenticates API requests |
| Refresh token | 7 days | Obtains new access tokens without re-login |
When an access token expires, the frontend silently obtains a new one using the refresh token. If the refresh token is also expired, the user is redirected to the login page.
Password Hashing
User passwords are hashed using bcrypt via the passlib library. Plaintext passwords are never stored or logged.
Duo MFA
Organizations can enable Duo Security multi-factor authentication. When configured, users must complete a Duo push, SMS, or TOTP challenge after entering their password. Duo MFA is configured per-organization by an admin.
Encryption at Rest
All sensitive fields are encrypted before being written to the database using Fernet symmetric encryption (from the cryptography library). Encrypted fields include:
- LLM provider API keys
- AWS access keys and secret keys
- Azure endpoint URLs
- OAuth client secrets for MCP server authentication
- Webex, Slack, and Teams bot tokens
The Fernet key is loaded from the environment (FERNET_KEY) and is never stored in the database or committed to source control.
The Fernet key is the root of trust for all encrypted data. If it is lost, all encrypted fields become unrecoverable. Back up this key securely and rotate it with a key migration process.
Tenant Isolation
GNETiX is a multi-tenant system. Every database table that contains tenant-specific data includes an org_id column, and every query is scoped to the requesting user's organization.
# Every query includes the org_id filter
result = await session.execute(
select(Agent).where(
Agent.org_id == current_user.org_id
)
)This is enforced at the application layer through dependency injection. The org_id is extracted from the JWT token and passed to every service function. There are no admin endpoints that bypass tenant scoping except for superuser-only operations explicitly designed for cross-tenant management.
Tenant isolation is a security boundary, not a convenience. Every database query must include an org_id filter. Omitting it is treated as a security defect.
Agent Call-Home Tokens
On-prem agents authenticate with the backend using call-home tokens:
- An admin registers an agent in the UI, which generates a 32-byte cryptographically random hex token.
- The token is displayed once to the admin for configuration on the agent.
- The backend stores only the bcrypt hash of the token -- the plaintext is never persisted.
- When the agent connects via WebSocket, it presents the token. The backend verifies it against the stored hash.
This ensures that even if the database is compromised, call-home tokens cannot be extracted or reused.
CORS
The backend enforces CORS restrictions, allowing requests only from the configured frontend origin. The wildcard origin (*) is never used.
# CORS is restricted to the frontend origin
allow_origins=[settings.frontend_url]Role Hierarchy
GNETiX uses a four-level role hierarchy. Each role inherits all permissions of the roles below it.
| Role | Level | Scope | Key Permissions |
|---|---|---|---|
user | 1 | Own org | Send messages, view permitted tools |
org_admin | 2 | Own org | Manage users, agents, MCP servers, LLM config, guardrails |
msp_admin | 3 | Multiple orgs | Manage multiple organizations (managed service provider) |
superuser | 4 | Global | Full system access, create organizations, manage all tenants |
Role checks are enforced on every API endpoint through dependency injection. Attempting to access an endpoint above your role level returns a 403 Forbidden response.
Platform Access Controls
In addition to roles, each user has per-platform access toggles:
| Toggle | Effect |
|---|---|
webex_enabled | User can interact via Cisco Webex |
slack_enabled | User can interact via Slack |
teams_enabled | User can interact via Microsoft Teams |
These toggles allow org admins to control which messaging platforms each user is authorized to use, independent of their role level.