Access model
How Fred decides who may do what: Keycloak identity, relationship-based authorization (ReBAC / OpenFGA) as the single enforcement mechanism, and a display-only role catalog for the UI.
Authorization in one line
Authentication is a Keycloak JWT (OIDC), validated on every request. Authorization is ReBAC (OpenFGA), and only ReBAC — evaluated at every endpoint against the caller's relationships to the object (their team, the resource owner, the tag hierarchy).
Source: fred-core security/authorization.py · security/permission_catalog.py
Identity & claims
A validated token is decoded into a KeycloakUser:
| Token claim | KeycloakUser field | Use |
|---|---|---|
| sub | uid | Stable user identity key |
| preferred_username | username | Display and tracing |
| User profile | ||
| resource_access[client].roles | roles | Role labels — feed the display catalog |
| groups | groups | Team membership — drives ReBAC relations |
Source: fred-core security/oidc.py (decode_jwt) · security/structure.py (KeycloakUser)
ReBAC — the enforcement model
Fred's OpenFGA schema defines object types, the relations users/teams can hold on them, and the permissions those relations grant. Every protected endpoint resolves to one of these checks.
Organization
Relations: admin, editor (= [user] or admin), viewer (= [user] or editor). Keycloak roles map onto these three. Global capabilities are organization permissions:
| Permission | Granted to |
|---|---|
| can_create_team | admin |
| can_create_agent | editor |
| can_edit_agent_class_path | admin |
| can_manage_platform · can_administer_users · can_run_benchmark | admin |
| can_read_kpi_global | admin |
| can_read_kpi · can_read_logs · can_read_metrics | viewer |
| can_read_opensearch · can_read_knowledge_graph · can_read_content | viewer |
| can_process_content | editor |
Team
Relations form an owner → manager → member hierarchy, plus an optional public marker:
owner=[user] or admin from organization;manager=[user] or owner;member=[user] or manager;public=[user:*].can_read=member or public;can_update_info,can_administer_members/managers/owners=owner;can_read_members,can_read_conversations=member.- Helper permissions
can_update_resourcesandcan_update_agents=manager— these flow into the object types below.
Agent, tag, document, resource
- agent —
owner: [user, team];read= owner or team member;update/delete= owner or the team'scan_update_agents. - tag —
owner/editor/viewer(each[user, team]) and aparenttag; grantsread,update,delete,share, with inheritance from the parent tag and the owning team'scan_update_resources. - document — has a
parenttag;readfollows the tag's read, andupdate/delete/processfollow the tag's update. - resource — like document:
read/update/delete/sharederived from its parent tag.
Source: fred-core security/rebac/schema.fga · security/rebac/rebac_engine.py
The RBAC display catalog
Actions & roles
Actions: create (C), read (R), update (U), delete (D), read:global (G), process (P). Roles:
admin— every action on every resource.editor— an allowlist per resource (matrix below).viewer— baselineread, with a few explicit overrides.service_agent— a narrow read-only subset; also an identity marker (not a ReBAC relation) that lets the evaluation worker act within a scopedteam_id.
Keycloak roles admin/editor/viewer are mapped onto the OpenFGA organization relations above; that mapping — not this table — is what enforces.
Resource matrix
| Resource | admin | editor | viewer | service_agent |
|---|---|---|---|---|
| tag | ALL | CRUD | R | R |
| document | ALL | CRU | R | R |
| documents_source | ALL | R | R | — |
| resource | ALL | CRUD | R | — |
| table | ALL | CRUD | R | R |
| tables_database | ALL | CRUD | R | R |
| kpi | ALL | R | R | — |
| opensearch | ALL | R | R | R |
| neo4j | ALL | — | R | — |
| logs | ALL | — | R | — |
| files | ALL | CRUD | CRUD | — |
| feedback | ALL | C | C | — |
| prompt_completions | ALL | C | C | — |
| metrics | ALL | R | R | R |
| agents | ALL | R | R | — |
| agent | ALL | — | R | — |
| sessions | ALL | CRUD | CRUD | — |
| message_attachments | ALL | C,R | CRUD | — |
| mcp_servers | ALL | CRU | R | — |
| user | ALL | R | R | — |
| team | ALL | — | R | — |
| organization | ALL | — | R | — |
Source: fred-core security/models.py (Action, Resource) · security/permission_catalog.py (ROLE_CAPABILITIES)
Endpoint enforcement
Authentication is a Keycloak JWT everywhere. Authorization is a ReBAC check keyed to the surface:
| Surface | Authorization (ReBAC) |
|---|---|
| control-plane · teams | Team permissions — can_read, can_update_info, can_read_members, can_administer_* |
| control-plane · platform, import/export, agent class path | Organization can_manage_platform |
| control-plane · global KPIs | Organization can_read_kpi_global |
| control-plane · sessions & history | Team + owner scoping — session.user_id == uid and matching team_id |
| control-plane · evaluations | Team can_update_agents / can_read |
| fred-agents · execute / stream | Pod-side team can_read on a mandatory team_id, per request |
| knowledge-flow · tags, documents, resources | Object-level tag / document / resource permissions |
| knowledge-flow · content, logs, statistics | Organization can_read_content / can_process_content / can_read_logs |
| health / readiness | Public — no auth |
Source: control-plane teams/service.py, product/service.py, evaluations/api.py · knowledge-flow features/* · fred-runtime app/agent_app.py
Execution authorization
Agent execution is team-scoped and authorized at the pod. There is no
signed grant or capability token: the control plane issues none. Identity is the
Keycloak JWT, and the agent pod runs an OpenFGA check on the caller's team_id for
every request before executing. The ExecutionGrantAction values (execute,
resume) are action labels, not tokens.
Source: fred-sdk contracts/execution.py · fred-runtime app/agent_app.py
ReBAC-disabled mode
When OIDC is off or ReBAC is disabled, Fred loads a no-op engine: has_permission()
returns True, and lookups return a "disabled" marker so services skip object-level
filtering. The deployment is effectively open — intended for local development, never production.
Source: fred-core security/rebac/noop_engine.py · security/rebac/rebac_factory.py