AegisAI exposes a single FastAPI surface that any AI copilot can talk to. Behind it, nine deterministic stages and four production-grade connectors enforce identity, policy, and masking before a single row leaves the backend.
HS256, RS256, ES256, PS256. JWKS rotation. Issuer and audience verification. Configurable clock-skew leeway.
Real BAPI_USER_GET_DETAIL walk: ACTIVITYGROUPS, PROFILES, AUTH_OBJECTS with field-value tuples. Wildcard support.
AWS simulate_principal_policy, Azure RBAC role-assignments, GCP testIamPermissions. Fail-closed on connector error.
Frequency, scope expansion, coverage growth, cross-user coordination. Trust score drives rate-limit bands and request restrictions.
Priority-weighted expression set evaluated by a safe AST whitelist. Deny-by-default, deny-wins-on-tie.
Intent compiles into SafeQuery{sql, params, entity, fields, tenant_id, row_policy_names}. :named placeholders only.
MODE-gated dispatch. PRODUCTION refuses to run the SAP simulator or in-memory fixtures. Identity propagates to the backend.
Schema-driven response firewall. Per-field classification × user clearance × auth-object gate. Drop / redact / hash / partial / aggregate.
Tamper-evident HMAC chain. Postgres-backed, row-locked append. Public /api/audit/integrity probe; admin-only /api/audit/verify.
Every connector uses the system's native authority surface. AegisAI does not maintain a parallel permission cache. If your SAP admin revokes a role at 09:00, AegisAI denies at 09:00.
Identity propagation via SAP Logon Ticket or user credentials. Authority lookup walks BAPI_USER_GET_DETAIL; per-object field-value enforcement via SUSR_USER_AUTH_FOR_OBJ_GET.
pyrfcPrincipal ARN resolved via STS GetCallerIdentity; permission checked via iam:SimulatePrincipalPolicy per request.
Authentication via DefaultAzureCredential (managed identity, environment, or CLI). Permission checked via Azure RBAC role-assignments at request scope.
Service-account credentials from a configured key file. Permission checked via testIamPermissions on the project resource.
/query looks like.A sales user asks for orders for company code 1000. Below is the literal HTTP shape — request, response envelope, and the audit row that gets persisted.
POST /query HTTP/1.1
Authorization: Bearer eyJ...<JWT>
Content-Type: application/json
{
"intent": "get sales bukrs 1000",
"context": {"system": "sap"}
}
HTTP/1.1 200 OK
Content-Type: application/json
{
"decision": "ALLOW",
"trust_score": 0.82,
"trust_level": "HIGH",
"trace_id": "9f8c2a4b91d3...",
"data": {
"sales": [
{"id": 1, "customer": "Acme",
"amount": 100, "bukrs": "1000",
"department": "sales"}
]
},
"trace": {"stages": [...]}
}
{
"id": 4291,
"trace_id": "9f8c2a4b91d3...",
"tenant_id": "tenant_a",
"user_id": "user1",
"decision": "allowed",
"intent": "get sales bukrs 1000",
"executed_query": "SELECT id, customer, amount,
bukrs, department FROM sales
WHERE tenant_id = :tenant_id
AND bukrs IN (:bukrs_0)",
"prev_hash": "a1b2c3...",
"row_hash": "d4e5f6...",
"hmac_sig": "0e1f2a..."
}
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"detail": "Trust restriction:
trust_block_scope_expansion: 'novel'"
}
# Audit row written with status="error",
# decision="denied". The hash chain extends
# the same way as for an ALLOW.