Skip to content

Profile

Definition

A Profile is a per-role capability, visibility, search, and federation configuration that scopes what a user — acting in that role — can see and do across the platform. Each Profile inherits from a base archetype (analyst, commander, principal) and supplies a delta over it. It is the object that ties a logged-in identity (matched by JWT realm roles / SSO groups) to the data they may read (ABAC visibility keys), the models they may query, the federates they reach, and the capabilities they hold (e.g. edition_manage). Profiles are authored as configuration, not created at runtime — they are written as YAML in loom, materialized into Elasticsearch, and read back by cortex per session.

Authored in loom/packs/domains/<name>/profiles.yaml; materialized by loom/loom/domain_loader.py. Runtime loader: cortex/cortex/core/capability_profiles.py; commands in cortex/cortex/profile.py. Persistence: a UDS DAO under the profile alias. The authored shape (documented in cortex/docs/PACK_CONFIG_REFERENCE.md:182) is much richer than the thin upstream schema stub in axonis-core/axonis/userspace/schemas.yaml:23.

Lifecycle

Not a stateful runtime object — no status field, no transitions. Its lifecycle is author → materialize → cache → activate: an operator edits the YAML; loom load-domains validates it, resolves the archetype, and explodes one profile document into the ES intelligence index (auto-injecting domain_pack, dataspace, accountability, and experience_pack references); cortex reads it via the Profile() DAO and caches it in Redis (PROFILE_CACHE_PREFIX = "cortex:profile:", capability_profiles.py:71); and a session "activates" one profile via set_profile(profile_id, session_id), storing the active profile_id in the Redis session (profile.py:324).

Journey through the code

Loaders in capability_profiles.py (load_profile, _fetch_profile_from_userspaceProfile().read(...), get_current_profile, _list_all_profiles, plus Redis cache get/set/delete). Commands in cortex/cortex/profile.py: get_profile_context, explain_result_limits, explain_visibility_boundary, set_profile, list_users (derives the user list from profile representative_user fields). Each is an MCP tool and REST route → command → Profile() DAO → REST → ES profile alias. The Profile is then consumed everywhere as the ABAC scope — for example, workflow.py reads profile.auth.roles_any to decide Task eligibility.

Data shape

Authored shape (PACK_CONFIG_REFERENCE.md:211): archetype, profile_id, profile_name, capabilities{} (overrides the archetype, e.g. edition_read / edition_manage), restrictions{max_query_size}, uds_source (federate alias), link_field (entity-resolution key), auth{roles_any, groups_any} (JWT realm roles / SSO groups), uds.visibility[] (ABAC read keys), search{enabled, default_uds_model, allowed_models[], preferred_dataspace, query_filters}, federation{enabled, federates[]}, plus the auto-injected domain_pack / dataspace / accountability / experience_pack, and representative_user{display_name, email, department}. Storage: ES intelligence index, profile alias.

Invariants

  • Inherits a valid archetype, resolved at load (domain_loader.py:203); the auto-injected refs must not be hand-set.
  • allowed_models restricts queries — empty or * means all; otherwise only the listed models.
  • Cortex enforces capability + model access only. Field-level ABAC is enforced by the UDS layer from token AUTHORIZATION markings, not by cortex (profile.py:264).
  • product.pack — a Profile belongs to a domain pack and references its Accountability, Experience, and Decision/Task templates.
  • product.taskprofile.auth.roles_any is the identity used for Task assignment and reviewer eligibility.
  • product.insight / product.edition — the active Profile scopes which investigations and editions a session can read and manage.

Open questions

  • Under-specified contract. A large gap separates the thin axonis-core/.../schemas.yaml:23 stub from the authored YAML shape; the canonical Profile contract is not pinned down.
  • Schema location split. cortex/server/api/schema/profiles.yaml no longer defines Profile (only QueryIntent; the Profile schema "moved to axonis-core"), so the runtime DAO's true schema is split and ambiguous.

Realized by: component.cortex.domain-loading, component.cortex.intelligence, component.cortex.pack-reference