modellerUpdated 2026-05-20

Configure Personas

What a persona is

A persona is a named subset of a model. It lists the measures, dimensions, and hierarchies that a given audience is allowed to query, and — optionally — a set of default filters that are merged into every query from that audience.

Row security decides which rows a caller sees. A persona decides which measures and dimensions they can even ask about. The two are orthogonal and compose: every rule from both layers fires on every query, in that order (persona first, then row security).

Use a persona when one model serves several audiences that should see different parts of the catalogue. Typical cases:

A model can carry any number of personas. Each is independent. They are never ORed together — a caller is tied to at most one persona, which is chosen by connecting to that persona's virtual catalog. The gateway emits one catalog per persona named <model.slug>_<persona.slug> alongside the base <model.slug> catalog. A seeded technical persona per model exposes every column (including hidden ones) as <model.slug>_technical, which is the classic modeller view.

The four fields

FieldMeaning
nameHuman label. Shown in the picker.
descriptionOptional free text. Shown as tooltip in the picker.
included_measure_idsAllow list of measure IDs. An empty list means no restriction — every measure is visible. A non-empty list means only those measures are visible.
included_dimension_idsAllow list of dimension IDs. Same empty-means-unrestricted rule.
included_hierarchy_idsAllow list of hierarchy IDs. Same rule.
default_filtersA small dictionary of dimension_path → value (or dimension_path → {operator: value}) applied to every query unless the caller has authored their own filter on the same dimension.
audience_rolesList of role names. A caller whose JWT carries one of these roles sees this persona listed in the picker.

Empty allow lists are the default and the most common setting for personas that exist only to attach default filters. Non-empty lists switch that axis into restrictive mode.

Hierarchy and dimension interaction

Dimensions are governance concepts; hierarchies are presentation objects built from dimensions. When a persona restricts the dimension allow list, any hierarchy level that is backed by an excluded dimension is automatically hidden. If all levels of a hierarchy are hidden, the hierarchy itself disappears from the list.

This means you do not need to maintain both dimension and hierarchy allow lists in lockstep. Restricting dimensions cascades into hierarchies naturally. The hierarchy allow list is still useful for hiding entire hierarchies that are allowed by their dimensions but should not be shown to the audience.

The Personas panel showing three personas for the acme demo: Finance, Ops, and Partner, with the Finance row expanded to reveal the three measure allow-list chips and a default filter on fiscal_year.

Figure 1 — The Personas panel. Each row shows the persona's name, description, and a one-glance summary of its allow lists and default filters. The preview-on-canvas action opens the model canvas with this persona pre-selected as a dimmed overlay.

How the Router applies a persona

Every query — from the gateway, the plugin execution endpoint, or internal REST paths — runs through the persona gate before row security and before route selection. The gate runs this four-step procedure:

  1. Resolve the persona. For gateway queries, the catalog name determines the persona. For the Excel plugin, the persona_id field in the request body is used. Embedded users always use the persona set in their token. The server determines the effective persona — the client sends a request, but the server decides.
  2. Check the allow lists. For each measure in the bound query, confirm its ID is in included_measure_ids (or the list is empty). Same for each dimension and hierarchy. The first object that falls outside is rejected with the object kind and name in the error detail.
  3. Merge default filters. Each (dimension_path, value) pair in default_filters is added to the query's WHERE — unless the caller has already authored their own filter on that dimension, in which case the user's filter wins.
  4. Proceed to row security. The persona step does not touch the generated SQL beyond adding filters. Row-security then wraps the plan as usual, and the aggregate/pocket fast paths remain available if neither layer blocks them.

The gate runs the same logic for every read path — REST, XMLA, JDBC, Excel plugin, MCP — so swapping tool is not a way around it.

Default filters

default_filters is a small dictionary keyed by dimension name. Each value is either:

Conflict rule: if the caller's query already has any filter on the same dimension, the persona's default is skipped. The user's filter wins. This lets a partner narrow a default filter without having their session collapse to zero rows because of a hidden default they cannot override.

Unknown operators are ignored silently; the rest of the dictionary still merges. This is deliberate — authoring errors should not block every query from an audience.

Canvas preview overlay

The model canvas has a Preview persona picker in the top-right corner (visible only when at least one persona exists). Selecting a persona:

The overlay is the single best way to catch mistakes before publishing. Select each persona in turn and scan for "I didn't mean to hide that".

The canvas in persona-preview mode for the Partner persona. Fact and dim tables that survive the Partner allow list render in colour; the internal Customer table, the Cost fact, and three dim tables are greyed out.

Figure 2 — Preview persona overlay. Dimmed tables and dashed joins show exactly what the selected audience cannot reach.

Audience roles and persona assignment

The audience_roles field on a persona controls which users are assigned to it. A user is assigned to a persona when their role appears in the persona's audience_roles list, or when the list is empty (available to everyone).

How persona assignment affects what happens when a user queries:

SituationWhat happens
User is assigned to one personaThat persona is applied automatically — no selection needed. Selecting a different persona is not allowed.
User is assigned to more than one personaThe user must select one. Queries without a selection are not allowed.
User is not assigned to any personaEverything in the model is available. The user may optionally select any persona as a voluntary filter.
Admin or modellerEverything in the model is available. Any persona may be selected optionally.
Technical persona holderThe technical persona is applied automatically. Selecting a different persona is not allowed.
Embedded user with a persona set in the tokenThat persona is always applied. No other persona can be selected.

The Query Panel, Measure Query Panel, and Excel plugin each show a Persona picker that lists the personas the user is assigned to. When only one is assigned, it is pre-selected. When none are assigned, the picker shows all available personas as optional filters.

External BI clients (Excel, Power BI, Tableau) pick a persona by connecting to its virtual catalog — the XMLA and JDBC catalog lists include one entry per persona (<model.slug>_<persona.slug>) alongside the base <model.slug> catalog.

The audience_roles list does not grant project access. A user still needs to be a member of the project to read the model. Persona assignment only controls which slice of the model catalogue the user sees.

Worked example — Finance, Ops, Partner

Context. One wholesale-orders model serves three audiences: the internal Finance team needs margin measures and access to every dimension; the internal Ops team needs utilisation and lead-time measures plus the operational dimensions; the external Partner integration can see shipment counts and dates but none of the cost or customer-identity columns.

Steps.

  1. Open the model in Model BuilderPersonas.
  2. Click New. Fill in:
  1. Repeat for Ops — measures utilisation, throughput, lead_time_days; dimension list empty; audience ops_manager.
  2. Repeat for Partner — measures shipments_count, delivered_on_time_pct; dimension allow list excludes customer.internal_id, customer.credit_score, and the margin-only dimensions; no default filter; audience partner_integration.
  3. Select each persona in the Preview persona picker on the canvas and confirm that only the intended tables stay in colour.
  4. Issue a test query from the Query Panel as a partner_integration user — the Persona picker should show Partner, and any attempt to reference a blocked measure should return a 403 with the measure name in the detail.

Interaction with row security

When both layers are configured:

  1. Persona runs first. If it denies the object, the query returns 403 before row security is touched.
  2. Persona default filters are added. These become part of the user's WHERE.
  3. Row security wraps the plan. The wrap intersects with the persona defaults — the caller sees rows that satisfy both.
  4. Fast paths are disabled only if row security fires. A persona that only trims the catalogue and adds a default filter does not disable aggregates or pockets; a row-security rule does.

In practice: personas are cheap, row security is expensive. Use a persona first to trim the catalogue; add row security only when row-level filtering is also needed.

Bypass row security (Phase 8.C.1)

A persona carries a Bypass row security flag. When enabled:

Use this only for internal dashboards that are already scoped through persona allow lists and that need the performance of the aggregate/pocket layer. A red warning appears in the editor while bypass is on, and saving with bypass newly enabled requires an explicit confirmation.

Per-persona aggregates and pockets (Phase 8.C.2 scaffolding)

Aggregates and pockets carry a nullable persona_id:

When a query is bound to persona X (through the catalog name), the router prefers a matching aggregate or pocket scoped to X over a global one of the same grain. This makes persona-specific workloads (e.g. a partner feed that only ever filters on a narrow slice) a first-class tuning target: the optimizer can build aggregates that are tight to that slice without poisoning global queries with a too-narrow cache.

query_logs and query_miss_logs now carry persona_id as well, so the optimizer can partition its workload scan when building candidates. The matcher precedence rule ships in v1; the optimizer-side scoping that populates per-persona aggregates ships in a follow-on slice.

v1 limitations

LimitationImpactWorkaround
Empty allow list means "no restriction".An editor who intends "nothing is allowed" must explicitly list every object they want hidden, or author the persona as an include list for the single measure that is allowed.Treat the empty-list case as documentation only. Use non-empty lists to express scope.
Default filters match on dimension name, not ID.Renaming a dimension breaks every default filter that references it.Keep a team-wide convention for dimension names; audit default_filters when renaming.
Audience-role matching is union-based.A user carrying multiple roles sees every persona that any of their roles advertises.Keep role strings narrow; avoid multi-audience superuser JWTs.
Per-connection binding ships via catalog name.Each persona is a separate virtual catalog (<model.slug>_<persona.slug>). External BI tools (Excel, Power BI, JDBC clients) pick a persona by connecting to the matching catalog, not by sending a header.Point each audience's connection string at the persona catalog intended for it.
No UI for default_filters yet.The panel shows the dictionary; edits happen through the JSON field or the API.A full editor ships in Phase 8.B.8+.

Troubleshooting

SymptomLikely causeFix
Persona picker is empty for a user who should see oneJWT has no role in the persona's audience_rolesAdd the role, or edit the persona's audience list
Query returns PERSONA_OBJECT_NOT_INCLUDED unexpectedlyA measure/dimension is not in the allow list, but the audience needs itAdd the object's ID to the list, or remove the list to make that axis unrestricted
Default filter does not fireThe caller already has a filter on that dimensionExpected — user-authored filters win. Remove the user filter or change the default's scope
404 PERSONA_NOT_FOUND on every queryThe request body carries a persona_id that belongs to another model, or the persona was deletedCheck the ID; the router checks persona.model_id == request.model_id
Persona catalog missing from Excel's database listCaller's JWT roles do not intersect the persona's audience_roles, and the caller is not an admin or modellerAdd the caller's role to audience_roles, or grant admin/modeller to allow impersonation
Canvas preview shows every table dimmedThe persona's allow lists are non-empty but the model's measure/dim IDs do not matchRe-select measures and dimensions from the canvas-side picker; IDs changed

Related