Fred supports relationship-aware authorization so users can keep resources private, share them with teams, or publish them broadly.
Important
Frequent deployment pitfall:
Keycloak app roles (admin/editor/viewer) are global RBAC roles.
Team rights (owner/manager/member) are ReBAC relations stored in OpenFGA.
Team resource updates require team relations (manager or owner) and are not automatically granted by app role alone.
In production, bootstrap team roles through an automated post-install step.
Use this mental model first:
- Each person has a global role that defines what they can do in the application overall.
- Each person also has a team role inside each team.
- To create, edit, or delete team content (for example a library), the team role is what matters.
- A global admin can still be blocked in a team if they are not team manager or team owner there.
- After installation, team managers/owners must be assigned automatically by deployment scripts.
Concrete examples:
- Alice is global admin but only team member in Thales: she can access the app, but cannot create a library in Thales.
- Bob is global editor and team manager in Northbridge: he can create/update libraries in Northbridge.
- Phil is global viewer and team member in Swiftpost: he can consult shared content but cannot manage team content.
- Fresh installation with no team manager/owner assigned: users can log in, but team management actions fail.
- Identity source: Keycloak manages users and teams (teams are Keycloak groups).
- Global app roles (RBAC):
admin/editor/viewerdefine application-wide capabilities. - Team/resource rights (ReBAC):
owner/manager/memberare relations in OpenFGA and control team-scoped operations. - Bridge object: Fred uses one organization object (
organization:fred) for global role context, without implicit team privilege escalation. - Deployment rule: post-install automation must create the required team relations; do not wait for manual UI actions.
Enabling it allows to;
- Enforce private libraries by default and explicitly share with collaborators.
- Express more than role checks (e.g.,a user or group “owner”/“member”/“viewer” on a specific library).
Without ReBAC, all resources (like librairies and documents) are public (all users can view, edit, delete... them).
This section is the locked design authority for all team configuration work. It states who can touch what at the product level. The technical enforcement mechanism is described in the sections that follow.
Responsible for team governance and platform-level safety.
Can:
- create a team and define its initial metadata
- assign and revoke the team manager role
- define and update
TeamPlatformPolicy(quotas, allowed model profiles, allowed MCP servers, storage and ingestion limits) - read any team configuration surface for audit purposes
Cannot:
- create, edit, or delete agent instances
- create, edit, or delete shared or personal prompts
- set or update
TeamRoutingPolicy
Responsible for what the team does with the platform within the bounds set by the platform admin.
Can:
- configure
TeamRoutingPolicy - manage shared team prompts (create, update, score, promote)
- manage team agent instances (enroll, tune, archive)
- read
TeamPlatformPolicyas a constraint — not editable
Cannot:
- modify
TeamPlatformPolicy - create teams or assign team roles
Can:
- use team-managed agents
- use prompts visible in their team context
- manage their own personal prompts
Cannot:
- configure any team-wide setting, policy, or shared resource
Out of scope for day-to-day product UI.
Design rule: no product contract may rely on implicit global-admin
escalation for team-scoped writes. A global admin who is not an explicit team
owner or manager is blocked for team-scoped writes. Every team-scoped write
must pass explicit team-scoped authorization.
The owner and manager roles are orthogonal on the business surface,
not hierarchical.
- An owner has full authority over governance and zero authority over agents, prompts, and routing policy.
- A manager has full authority over agents, prompts, and routing policy and zero authority over platform guardrails.
- Neither role grants implicit access to the other's surfaces.
The only exception: team creation and manager assignment belong exclusively to
the platform admin (owner).
This rule must be enforced at the API layer. It is not sufficient to rely on UI-level restrictions.
Fred uses OpenFGA as the ReBAC engine (compatible with the Zanzibar model).
- Deployment guidance for OpenFGA: https://openfga.dev/docs/getting-started/setup-openfga/overview
- Deploy OpenFGA (localy, with Docker, on Kubernetes...) and provide an API token to the ReBAC engine (see
token_env_varbelow).
Keycloak options (see KEYCLOAK.md for more details):
- The
knowledge-flowandagenticclient needsrealm-management: query-users, query-groups, view-usersandaccount: view-groupsto be able to list users and groups from Keycloak knowledge-flowneeds in additionrealm-management: manage-usersclient roles to be able to add/remove users from groups- Keycloak must send the
groupsclaim in access tokens (seegroups-scopeclient scope in KEYCLOAK.md).
Fred uses a singleton organization node in ReBAC:
- Object id:
organization:fred - Purpose: hold global application role context (
admin/editor/viewer) without automatically turning these roles into team owner/manager rights.
How it works:
- At authorization time, the backend derives contextual relations from the user token (for example: user has
adminrole on organization). - Team checks rely on persistent tuples linking teams to the organization:
organization:fred#organization@team:<team_id>
- Team permissions still require explicit team relations (
owner/manager/member) for the target team.
Important consequence:
- A global
adminuser can still be denied team operations when they are not explicitownerormanagerof that team. - Deployment must bootstrap team
owner/managerrelations during post-install.
Here is the minimal configuration to enable ReBAC (Agentic/Knowledge Flow):
security:
# ...
rebac:
type: openfga
api_url: "http://localhost:9080"And set OPENFGA_API_TOKEN in the environment.
By default the backend will create the store (if missing) and push the Fred authorization model at startup. You can turn that off (with create_store_if_needed and sync_schema_on_init) if you manage OpenFGA yourself. In that case, we recommend you to pass a a static authorization model id with authorization_model_id.
security:
# ...
rebac:
enabled: true # Set false to bypass ReBAC (warning: all private resources will become public)
type: openfga
api_url: "http://localhost:9080" # OpenFGA HTTP endpoint
store_name: "fred" # OpenFGA store name. Reuse the same store across services
create_store_if_needed: true # Automates store bootstrap (disable if pre-provisioned)
sync_schema_on_init: true # Pushes the default Fred authorization model
authorization_model_id: null # Authorization model id to use in case `sync_schema_on_init: false`
token_env_var: "OPENFGA_API_TOKEN" # Env var holding the bearer token
timeout_millisec: 2000 # Optional request timeout
headers:
# Optional static headers sent to OpenFGA
X-Custom-Header: "value"