← Auth / Security

Authorization and ACL

auth / security · workzone
1 Roles → Admin Panel Who the user is on the platform.
Owner
  • Full access
  • Manage admins
  • Critical settings
Admin
  • Users
  • Data sources
  • Company agents
  • Monitoring
Member
  • Queries
  • Personal agents
  • Data within ACL
Team Lead — a capability, not a role v2 → Agent Engine
A Member gains rights over their team: managing its agents and settings.
2 Permission check Whether this action is allowed.
The require() abstraction
An endpoint checks for the required permission, not a specific role.
how
In v1, require() maps a permission to a role via the static ROLE_PERMISSIONS table. The fine-grained permissions from v2 plug in here too.
RBAC middleware
A FastAPI dependency — the single check point: it takes the required permission, not a role. Roles are never checked directly on endpoints.
role change
The role is taken from the access token's claim — its freshness and DB reads for critical checks are described in JWT claims.
Resource ownership check
RBAC answers “is this action allowed at all”, ownership — “is it over your own object”. Personal agents and API keys are accessible only to their owner: the endpoint checks the resource's owner_id against the current user_id on top of the permission. IDOR protection.
v2 Team ownership — members' access to their team's resources — arrives with the Team Lead capability.
why on top of RBAC
Without an ownership check, one Member could edit another's agent — a classic IDOR. Owner / Admin bypass the ownership check only where it is their normal remit (user management), never to access source data.
Boundary of admin rights
Owner and Admin see data strictly within source ACL, just like a Member. There is no role-based blanket ACL bypass.
break-glass and audit
Emergency access that bypasses source ACL is a separate, explicit setting with a mandatory log entry. Admin Panel.
Granular permissionsv2
The permissions and user_permissions tables provide fine-grained rights on top of roles.
details and precedence
They let an individual user's rights be extended or trimmed. Assigned via Admin Panel. Precedence is unambiguous: an explicit deny outweighs any grant — whether from a role or a targeted grant. Grants (role + extensions) are summed first, then deny is subtracted.
3 Identity A user's external identities.
The identity_mapping table → Data model
Links a platform user to their external login identities — a universal sign-in bridge: messenger bots (source='slack' · 'telegram' · 'mattermost', v1), SSO providers (Okta, Azure AD, v2). Fields and constraints — in the data model.
population
Empty at start — populated on sign-in through any source: in v1 that's the Slack, Telegram and Mattermost bots.
Identities from content sources — in Knowledge Store → Knowledge Store
Who the user is in Jira / Slack / Confluence is a source_principalidentity in Knowledge Store, not here. The bridge to the platform account is identity.user_id → users: this keeps login federation and content identities from mixing in one table.
Consolidating identities by email → Knowledge Store
Source principals are consolidated into a single identity only by exact email; Harvester produces it. Fuzzy “different emails, same person” is v2 (deep entity resolution).
unmatched
No email, a collision, or ambiguity — the record stays unmatched and goes to manual linking by an admin via Admin Panel.
Bridge to the account — auto-link by email → Knowledge Store
When a person is created on the platform — invite accepted, setup, or an email change by an admin — their users row is linked to an already existing identity in the same Knowledge Store consolidation by exact email (identity.user_id). That way the newly signed-in user immediately sees their source accounts. The rule and storage live in Knowledge Store; here — the trigger moment from the sign-in side.
address didn't match
Sign-in email ≠ source email — the identity stays without a bridge and is linked manually in Admin Panel; fuzzy auto-consolidation across different addresses is v2.
4 Data ACL → Knowledge Store → Harvester → Query Engine What is available to the user.
Approach — Hybrid, mapping at query time
Source ACL is stored in the source's original terms in Knowledge Store. At query time the platform resolves the user's accounts and pre-filters the results: the query is driven by Query Engine, the filter runs in the store in a single SQL statement.
principle
Who sees what is decided solely by the source itself.
Container-level ACL
Permissions are set on containers (a Jira project, a Confluence space, a Slack channel), and nested entities inherit them.
override
Restrictions on an individual document outweigh container ones and override them.
Double filterv2
Access is checked twice: against source ACL and against platform restrictions Admin Panel. Both must clear it.
Fail-safe
The overarching principle of the whole layer: access is closed by default. Any ambiguity, check error, or missing permission data resolves in favor of denial — better to hide something available than to show something forbidden.
5 Storage → Knowledge Store Where the ACL physically lives.
ACL storage
  • PostgreSQL — the entity_acl table (source of truth)
  • Postgres graph — node/edge tables in the same database; a single ACL JOIN covers traversal too, with no separate rights projection
  • pgvector — the ACL filter SQL WHERE/JOIN narrows the set before the search → vector search top-N already runs over the permitted set (pre-filter, not post-hoc culling)
Side entrance
API key · OAuth
key / token → user_id → role / ACL
API keys and OAuth — described in Authentication.
One thing matters here: any such sign-in resolves to a user_id and then follows the usual path.
folds into layer 2
key / token resolves to user_id → permission check → ACL