Aegis Access Control Model
This guide describes the access control model used in the Aegis Platform, enabling secure, tenant-aware delegation of access across LLM calls, agent runs, and data queries — all while ensuring robust isolation and attribution for billing and audit.
Aegis supports complex workflows where agents act on behalf of users in a tenant. These agents may:
- 🔍 Fetch and search tenant-specific data
- 💬 Call LLM APIs through a proxy (for usage tracking)
- ⚙️ Perform actions dependent on user or group context
These agent workflows require the platform to:
- Determine who is making the request (the subject)
- Identify which tenant’s data or resources are being accessed or modified (the target) — this could be a document, a chunk, a user session, or a team of agents
- Enforce access rules that determine whether the subject is allowed to act on the target, based on tenant boundaries, roles, and delegation rules
The target tenant represents the scope of ownership for the resource involved in the request. For example:
- If a tenant admin retrieves documents for their users, the target tenant is their own tenant
- If a superuser retrieves agent runs for audit across all tenants, the target tenant is specified per request and can differ from the subject
- If a M2M client uploads data or indexes content, the target tenant determines where that data resides
Correctly resolving and validating the target tenant is crucial to:
- Enforcing isolation between tenants
- Preventing unauthorized cross-tenant access
- Ensuring that usage (e.g., LLM tokens, data access) is billed to the correct account
Importantly, the Gateway cannot and should not determine the full scope of what an API request might do internally — for example, calling downstream services, triggering async workflows, or touching multiple resources. As such, access rights must be normalized at the start of the request and preserved throughout the call chain.
To achieve this:
- The Gateway performs authentication and constructs a signed, scoped
AccessRights
object. - Downstream services use this object to enforce tenant isolation, regardless of the internal complexity of the workflow.
🔄 Request Context via x-access-request
The Gateway supports an optional x-access-request
header that allows the client to explicitly declare who the request is acting on behalf of.
Example header (base64-encoded JSON):
{
"tenant_id": "t1",
"user_id": "u42"
}
Gateway Behavior:
-
If present:
- Decode
x-access-request
- Validate that the requester (subject) is allowed to act on the declared tenant/user
- Construct
x-access-rights
based on this declared execution context
- Decode
-
If missing:
- Default to acting on behalf of the authenticated subject themselves
The Gateway never inspects or validates request bodies — it only uses this header for target context.
This allows clients (especially superusers or internal M2M clients) to safely delegate execution across tenants, while preserving strict isolation rules.
🔐 Header Security
- All requests must be sent over HTTPS to ensure that sensitive headers are protected in transit.
- The Gateway signs or serializes
x-access-rights
to prevent tampering. - Application-layer encryption (e.g., JWE) is not required, but can be considered if access context includes sensitive user-specific metadata or is passed across trust boundaries.
🧩 What This Model Solves
To enable this securely, Aegis:
- Clearly defines the requesting subject
- Identifies the target tenant or data
- Validates that the request is authorized based on roles and scope
This allows for scenarios like:
- A tenant user launching an agent that only retrieves their data
- A M2M client generating completions scoped to a tenant
- A platform superuser auditing any tenant’s agent activity
✨ Design Goals
- Enforce strict tenant isolation by default
- Centralize policy enforcement at the Gateway
- Allow system-wide access via privileged super roles
- Make internal services stateless and policy-agnostic
- Enable agents to securely delegate calls to LLM and storage systems
- Attribute usage per tenant for billing and auditing
🏢 Tenancy Model
- Tenant: A customer or organization
- Group: A sub-unit within a tenant (e.g., department)
- User: An individual within a tenant
👤 Identity Types
Type | Description |
---|---|
Tenant User | Can only act within their own tenant and group |
Tenant Admin | Can manage users and workflows within their tenant |
M2M Client | Authenticated service client tied to one tenant |
Super User | Platform admin with access to all tenants |
Super M2M Client | System client with access to all resources |
🔐 AccessRights Model
Constructed centrally at the Gateway after authentication:
class AccessRights(BaseModel):
tenant_id: str
group_id: Optional[str] = None
user_id: Optional[str] = None
roles: List[str] = []
is_super: bool = False
allowed_tags: List[str] = []
permissions: List[str] = []
subject_user_id: Optional[str] = None # For superusers acting on behalf of others
This is serialized (e.g. base64 JSON) and forwarded via:
x-access-rights: <encoded>
🔐 Authentication vs Authorization
Aegis supports:
- Auth0-based OAuth authentication
- Internal JWT login
- API key-based authentication for M2M clients
Regardless of how the requester authenticates:
✅ The Gateway normalizes access using the AccessRights
model
✅ It applies tenant boundaries and permissions before routing
✅ Downstream services rely only on x-access-rights
— never on raw tokens
🎯 Target Tenant ID
The target tenant is the tenant whose data the request is affecting — which may not be the same as the subject making the request.
Examples:
Scenario | Subject Tenant | Target Tenant | is_super | Access Allowed |
---|---|---|---|---|
Tenant user accessing their own data | tenant_a | tenant_a | ❌ | ✅ |
M2M client acting for their tenant | tenant_b | tenant_b | ❌ | ✅ |
Admin trying to access a different tenant | tenant_b | tenant_c | ❌ | ❌ |
Super user querying another tenant | platform | tenant_x | ✅ | ✅ |
Key Rule
🛑 Cross-tenant access is forbidden unless
is_super = True
.
⚖️ Enforcement Logic
This logic runs in the gateway to block invalid cross-tenant access:
def validate_access(subject: AccessRights, target_tenant_id: str) -> None:
if subject.tenant_id != target_tenant_id:
if not subject.is_super:
raise HTTPException(status_code=403, detail="Cross-tenant access forbidden")
✈️ Gateway Flow
-
✅ Authenticate the request (JWT / OAuth / API key)
-
🧾 Extract subject claims (tenant, user, roles)
-
📥 Parse
x-access-request
(if present)- If present, validate declared target context
- If not present, use subject’s own identity as target
-
🔒 Validate tenant-level access rules
-
📦 Construct
AccessRights
object -
📤 Inject it into
x-access-rights
header -
🔁 Forward to downstream internal service
🛠️ What Internal Services Do
Downstream services (e.g. storage, LLM proxy, agent orchestrator):
- Read
x-access-rights
- Apply tenant/group filters to any queries or actions
- Trust only the gateway’s enforcement — never parse raw tokens
- Never accept user-supplied
tenant_id
; derive it fromAccessRights