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_userspace → Profile().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_modelsrestricts 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).
Related products
product.pack— a Profile belongs to a domain pack and references its Accountability, Experience, and Decision/Task templates.product.task—profile.auth.roles_anyis 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:23stub from the authored YAML shape; the canonical Profile contract is not pinned down. - Schema location split.
cortex/server/api/schema/profiles.yamlno longer defines Profile (onlyQueryIntent; 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