PII Attribute
The [PII] attribute marks data as personally identifiable information (PII) under GDPR. When Chronicle sees this attribute on an event property or a ConceptAs<T> type, it encrypts the value automatically when the event is written to the event log and decrypts it transparently on read.
using Cratis.Chronicle.Compliance.GDPR;Attribute definition
Section titled “Attribute definition”[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Parameter)]public sealed class PIIAttribute(string details = "") : AttributeThe optional details parameter lets you record why the value is classified as PII — for example, the legal basis under which it is collected or the retention period. This information is stored in the event schema and can be used by compliance reporting tools.
[PII("Collected under GDPR Art. 6(1)(b) — necessary for contract performance")]public record PersonName(string Value) : ConceptAs<string>(Value);Where the attribute is valid
Section titled “Where the attribute is valid”| Target | Supported | Notes |
|---|---|---|
ConceptAs<T> class | Yes | Preferred approach — marks the concept type itself as PII |
| Event property | Yes | Marks a single property of an event as PII |
EventSourceId or EventSourceId<T> | No | Throws PIINotSupportedOnEventSourceId at runtime |
| Any other class type | No | Throws PIIAppliedToNonConceptAsType at runtime |
Applying to an event property
Section titled “Applying to an event property”You can mark a single property on an event record as PII. This is useful when the property type is a primitive and you cannot or do not want to introduce a dedicated concept type.
[EventType]public record EmployeeRegistered( [PII] string FirstName, [PII] string LastName, string Department);When this event is written, FirstName and LastName are encrypted. Department is stored as plaintext.
Applying to a ConceptAs type
Section titled “Applying to a ConceptAs type”The preferred approach is to mark the ConceptAs<T> type itself as PII. Every property across every event that uses this type is then automatically encrypted — you declare the rule once and it applies everywhere.
[PII]public record PersonName(string Value) : ConceptAs<string>(Value){ public static readonly PersonName NotSet = new(string.Empty); public static implicit operator string(PersonName name) => name.Value; public static implicit operator PersonName(string value) => new(value);}Constraints
Section titled “Constraints”EventSourceId is not supported
Section titled “EventSourceId is not supported”Applying [PII] to a type that inherits from EventSourceId or EventSourceId<T> throws PIINotSupportedOnEventSourceId at runtime:
// ❌ This will throw PIINotSupportedOnEventSourceId[PII]public record EmployeeId(Guid Value) : EventSourceId<Guid>(Value);Event source identifiers are used to look up encryption keys and group events. Encrypting them would make key lookup impossible. If the identifier itself is sensitive, use a non-sensitive surrogate (such as a random Guid) as the event source identifier and store the sensitive value in a [PII]-marked event property.
Non-ConceptAs class types are not supported
Section titled “Non-ConceptAs class types are not supported”Applying [PII] to a class that does not inherit from ConceptAs<T> throws PIIAppliedToNonConceptAsType at runtime:
// ❌ This will throw PIIAppliedToNonConceptAsType[PII]public class SomeArbitraryClass { }The [PII] attribute at the class level is reserved for ConceptAs<T> types. Use a property-level [PII] annotation instead if you need to mark individual fields on a class.
Details parameter
Section titled “Details parameter”The details parameter is a free-text description stored in the event schema. It is never used for encryption — it exists solely to record why a value is classified as PII for compliance documentation and auditing purposes.
[PII("Full legal name — required for contract identification")]public record LegalName(string Value) : ConceptAs<string>(Value);