Repository navigation
Description
Overview
MutationContext currently keeps the actor type as an internal property:
internal ActorType ActorType { get; init; }This means that consumers of ModularityKit.Mutator cannot determine whether mutation was initiated by a User, Service, or System. This becomes limitation for external mutation policies and governance components that need to make security or authorization decisions based on the mutation execution context.
Problem
The framework already provides explicit actor types through:
MutationContext.System(...)
MutationContext.User(...)
MutationContext.Service(...)However, external consumers cannot inspect the resulting actor type. For example, financial ledger application may need to enforce rule such as:
User nitiated mutations must not be allowed to post entries to protected system accounts, while system level mutations may be allowed.
The mutation policy has access to:
mutation.Contextbut cannot inspect:
mutation.Context.ActorTypebecause ActorType is internal.
As a result, consumers may be forced to rely on application specific conventions, such as checking string prefixes:
var isSystemActor = transaction.InitiatedBy.StartsWith(
"system:",
StringComparison.OrdinalIgnoreCase);This is fragile because domain or audit data should not be used as the authoritative source for mutation authorization decisions.
Proposed solution
Expose the actor type for reading while keeping mutation of the property restricted to the framework.
Change:
internal ActorType ActorType { get; init; }to:
public ActorType ActorType { get; internal init; }This would allow external consumers to inspect the actor type:
if (mutation.Context.ActorType == ActorType.System)
{
// System level mutation
}while preventing external code from directly assigning the value:
context.ActorType = ActorType.System; // still not allowedThe framework would still retain control over how the actor type is assigned.
Optional convenience API
Alternatively, or in addition, MutationContext could expose convenience properties:
public bool IsSystemActor =>
ActorType == ActorType.System;
public bool IsUserActor =>
ActorType == ActorType.User;
public bool IsServiceActor =>
ActorType == ActorType.Service;This would allow policies to express their intent more clearly:
if (!mutation.Context.IsSystemActor)
{
return PolicyDecision.Deny(
"Non system actors cannot modify protected accounts.",
Name);
}Additional consideration: System actor identity
MutationContext.System() currently creates system context without an ActorId:
MutationContext.System("Post settlement transaction");Resulting conceptually in:
ActorType = System
ActorId = null
For auditing and governance purposes, it may be useful to identify the specific system component responsible for the mutation. For example:
MutationContext.System(
systemId: "ledger-service",
reason: "Post settlement transaction");This would allow downstream consumers to distinguish between different system components while still treating them as system level actors. For example:
ActorType = System
ActorId = ledger-service
Reason = Post settlement transaction
This could be particularly useful in distributed systems where multiple internal services perform mutations.
Expected outcome
External mutation policies should be able to reliably distinguish between:
UserServiceSystemUnknown
without relying on application specific string conventions or domain data.
This would make MutationContext more useful for:
- Security policies
- Authorization decisions
- Governance rules
- Audit trails
- Compliance controls
- Financial domain policies
- Multi service mutation workflows
The key principle is that MutationContext should provide the authoritative execution context, while domain entities such as LedgerTransaction should remain responsible for storing audit information about the resulting mutation.
Activity
rian-be commentedon Jul 28, 2026
Decision on convenience booleans (
IsSystemActor,IsUserActor,IsServiceActor)These were initially implemented as properties on
MutationContextfor convenience:Trade off
MutationContextis data object, not policy logicctx.IsSystemActorvsctx.ActorType == ActorType.SystemAdministratortreated as "user"), the bool becomes misleadingNote
Decision
Kept out of
MutationContext. The context is data holder policy logic belongs in policies. Consumers writecontext.ActorType is ActorType.System or ActorType.Schedulerdirectly explicit and not misleading when new actor types are added.