@@ -39,7 +39,7 @@ case class CallContext(
3939 // the creator is the granting human (they create their own consent in the Portal).
4040 // Not set by Berlin Group / UK flows, where the consent may be created by a TPP flow
4141 // with no human logged in — see `consenter` for those.
42- // Read via humanUser / effectiveHumanUserId , where it takes precedence over consenter.
42+ // Read via humanUser / accountableUserId , where it takes precedence over consenter.
4343 onBehalfOfUser : Box [User ] = Empty ,
4444 // The human (PSU) who AUTHORISED the consent this request runs under — the owner of
4545 // record, from the consent table's userId (bound by updateConsentUser during the
@@ -113,7 +113,7 @@ case class CallContext(
113113 * Anything that must name a human rather than a principal reads this instead: the CBS adapter,
114114 * which tells the core banking system who is asking, and the consent ownership checks.
115115 * Stored data (metric rows included) always carries the authenticated principal; the human is
116- * resolved at read time via the consent table (see effectiveHumanUserId ).
116+ * resolved at read time via the consent table (see accountableUserId ).
117117 */
118118 def humanUser : Box [User ] = onBehalfOfUser.or(consenter).or(user)
119119
@@ -182,7 +182,7 @@ case class CallContext(
182182 // (CallContext.user), never a resolved human. Under a consent that principal is the
183183 // consent's own shadow user (a per-consent UUID with an empty name) — the on-behalf-of
184184 // human is not stored here but resolved at read time via the consent table
185- // (consentReferenceId below -> consent.userId), see CallContext.effectiveHumanUserId .
185+ // (consentReferenceId below -> consent.userId), see CallContext.accountableUserId .
186186 userId = this .user.map(_.userId).toOption,
187187 userName = this .user.map(_.name).toOption,
188188 consumerId = this .consumer.map(_.consumerId.get).toOption,
@@ -217,22 +217,31 @@ case class CallContext(
217217 def userId : String = user.map(_.userId).openOrThrowException(AuthenticatedUserIsRequired )
218218
219219 /**
220- * The human User this request is really about.
220+ * The ACCOUNTABLE identity this request is really about — the user_id that durable
221+ * state (creator role grants, account holders, entitlement requests) and attribution
222+ * (metrics families, "my" queries) bind to. "Accountable" deliberately hints at a
223+ * legal person: today resolution always ends at the human who granted the consent,
224+ * but the contract is accountability, not species — if durable, sponsored agent
225+ * identities are ever admitted as principals in their own right, resolution may stop
226+ * at such an agent without this name becoming a lie (unlike the previous name,
227+ * effectiveHumanUserId).
221228 *
222- * The authenticated `user` may be the human themselves, or an agent user minted by a
223- * Consent the human granted (e.g. Opey / MCP acting under a consent). Resolution order:
229+ * The authenticated `user` may be the accountable party themselves, or a consent user
230+ * minted by a Consent they granted (e.g. Opey / MCP acting under a consent) — consent
231+ * users are ephemeral and must never hold durable state (see addEntitlement's guard).
232+ * Resolution order:
224233 * 1. `onBehalfOfUser` or `consenter`, when a middleware populated them (free);
225234 * 2. otherwise resolve via the delegation registry: the caller's ResourceUser row's
226235 * CreatedByConsentId names the Consent that minted it, and that Consent's userId
227236 * names the granting human;
228- * 3. otherwise the caller IS the human .
237+ * 3. otherwise the caller IS the accountable party .
229238 *
230239 * IMPORTANT: this reads only the authenticated user and server-written columns
231240 * (ResourceUser.CreatedByConsentId, MappedConsent.mUserId). It deliberately takes no
232241 * parameters so nothing caller-asserted (body/header/query values) can ever influence
233242 * the resolution — identity-sensitive queries (e.g. /my/banks) depend on that.
234243 */
235- def effectiveHumanUserId : String = {
244+ def accountableUserId : String = {
236245 val delegatedHumanUserId = onBehalfOfUser.or(consenter).map(_.userId).filter(_.nonEmpty)
237246 delegatedHumanUserId.openOr {
238247 val authenticatedUserId = user.map(_.userId).openOr(" " )
0 commit comments