Subject
The Subject is Chronicle's compliance identity — the value used to key per-subject material such as PII encryption keys. When you append an event that contains [PII]-annotated properties, Chronicle encrypts those properties under the subject's key. Selecting the correct subject on the command ensures that events land under the right encryption key so they can be decrypted later.
When no explicit subject is supplied, Chronicle defaults to the EventSourceId. Setting a subject on the command is only necessary when the compliance identity differs from the aggregate identity — for example, when a command mutates an order aggregate but the PII belongs to the customer.
Resolution Order
Arc resolves the subject from the command in this order:
- Return a
SubjectfromHandle()as part of the response tuple. - Implement
ICanProvideSubjecton the command record and return the subject fromGetSubject(). - Add a property of type
Subjectto the command. - Decorate a property with
[Subject]fromCratis.Chronicle— Arc reads its value and converts it to aSubject.
If none of these are present, no subject is passed to Chronicle and it falls back to using the EventSourceId.
Returning Subject from Handle
The simplest approach when the subject is computed inside the handler:
using Cratis.Arc.Commands;
using Cratis.Chronicle;
using Cratis.Chronicle.Events;
[Command]
public record PlaceOrder(EventSourceId OrderId, CustomerId CustomerId, decimal Amount)
{
public (OrderPlaced, Subject) Handle() =>
(new OrderPlaced(OrderId, CustomerId, Amount), new Subject(CustomerId.Value.ToString()));
}
[EventType]
public record OrderPlaced(EventSourceId OrderId, CustomerId CustomerId, decimal Amount);
Arc detects the Subject in the tuple response and passes it to Append automatically. The Subject is treated as append metadata, so it does not become the command response even when you also return another response value in the same tuple.
ICanProvideSubject Interface
Use ICanProvideSubject when the subject is derived from command properties and you want an explicit, discoverable contract:
using Cratis.Arc.Commands;
using Cratis.Arc.Chronicle.Commands;
using Cratis.Chronicle;
using Cratis.Chronicle.Events;
[Command]
public record PlaceOrder(EventSourceId OrderId, CustomerId CustomerId, decimal Amount)
: ICanProvideSubject
{
public Subject GetSubject() => new(CustomerId.Value.ToString());
public OrderPlaced Handle() => new(OrderId, CustomerId, Amount);
}
[Subject] Attribute on a Property
When a command property directly represents the compliance identity, mark it with [Subject] from Cratis.Chronicle. Arc reads the property value and converts it to a Subject:
using Cratis.Arc.Commands;
using Cratis.Chronicle;
using Cratis.Chronicle.Events;
[Command]
public record PlaceOrder(EventSourceId OrderId, [Subject] CustomerId CustomerId, decimal Amount)
{
public OrderPlaced Handle() => new(OrderId, CustomerId, Amount);
}
If the property is already of type Subject, Arc uses it directly even without [Subject]. Any other [Subject]-annotated type is converted via its ToString() representation.
Relationship to PII Decryption
Chronicle uses the subject as the lookup key for PII encryption keys. When a read model contains [PII]-annotated properties, Arc uses the same subject in two places:
- query responses, where Arc's PII interceptor calls
Release()before the read model is served to the client - command-side read model dependencies, where Arc resolves the subject from the current command context and releases the injected read model before your handler or validator uses it
The subject set here at append time must therefore match the subject used on the read model's [Subject]-marked property.