Skip to content

Audit Trail

Tayra provides a built-in audit trail system for GDPR Article 30 compliance. Every encryption, decryption, key creation, key deletion, and crypto-shredding operation is logged as a structured audit event. This gives you a complete record of all processing activities involving personal data encryption keys.

GDPR Article 30 — Records of Processing Activities

Article 30 of the GDPR requires organizations to maintain records of processing activities that involve personal data. Tayra's audit trail satisfies this requirement by recording:

  • What happened (event type)
  • When it happened (timestamp)
  • Which key was involved (key ID)
  • Whose data was affected (subject ID)
  • What entity type was processed (entity type)
  • How many fields were involved (field count)

Setup

The DefaultTayraAuditLogger is registered automatically when you call AddTayra(). It writes structured log entries via ILogger and emits ActivityEvent entries on the current OpenTelemetry activity:

cs
var auditServices = new ServiceCollection();
auditServices.AddLogging();
auditServices.AddTayra(opts => opts.LicenseKey = licenseKey);

// The DefaultTayraAuditLogger is registered automatically by AddTayra().
// It logs structured audit events via ILogger and emits ActivityEvents.
// To replace it with a custom implementation:
auditServices.AddSingleton<ITayraAuditLogger, ConsoleAuditLogger>();
anchor

To disable audit logging, register the NullTayraAuditLogger:

csharp
services.AddSingleton<ITayraAuditLogger>(
    Tayra.Audit.NullTayraAuditLogger.Instance);

Custom Audit Logger

Implement the ITayraAuditLogger interface to send audit events to your compliance system:

cs
/// <summary>
/// Custom audit logger that writes events to the console.
/// In production, you might write to a database, message queue,
/// or external audit system (e.g., Splunk, Datadog, Azure Monitor).
/// </summary>
public class ConsoleAuditLogger : ITayraAuditLogger
{
    public void LogEvent(TayraAuditEvent auditEvent)
    {
        Console.WriteLine(
            $"  [AUDIT] {auditEvent.Timestamp:O} {auditEvent.EventType} " +
            $"KeyId={auditEvent.KeyId} SubjectId={auditEvent.SubjectId} " +
            $"EntityType={auditEvent.EntityType} FieldCount={auditEvent.FieldCount}");
    }
}
anchor

The ITayraAuditLogger interface has a single synchronous method:

csharp
public interface ITayraAuditLogger
{
    void LogEvent(TayraAuditEvent auditEvent);
}

Synchronous by Design

The LogEvent method is synchronous to avoid adding async overhead to every encryption operation. Implementations should be fast and non-blocking. If you need to write to a database or external service, buffer events in memory and flush asynchronously (e.g., using a Channel<T> or background service).

Audit Event Model

The TayraAuditEvent record contains all the context for a single audit entry:

cs
// TayraAuditEvent is a record with the following properties:
var exampleEvent = new TayraAuditEvent
{
    EventType = TayraAuditEventType.DataEncrypted,
    Timestamp = DateTimeOffset.UtcNow,         // Auto-set to UtcNow
    KeyId = "patient-abc123",                   // The encryption key involved
    SubjectId = "abc123",                       // The data subject identifier
    EntityType = "PatientRecord",               // The .NET type being processed
    FieldCount = 3,                             // Number of PII fields encrypted
    Details = "Encrypted during save",          // Free-text details
};

Console.WriteLine($"  EventType: {exampleEvent.EventType}");
Console.WriteLine($"  Timestamp: {exampleEvent.Timestamp}");
Console.WriteLine($"  KeyId: {exampleEvent.KeyId}");
Console.WriteLine($"  SubjectId: {exampleEvent.SubjectId}");
Console.WriteLine($"  EntityType: {exampleEvent.EntityType}");
Console.WriteLine($"  FieldCount: {exampleEvent.FieldCount}");
Console.WriteLine($"  Details: {exampleEvent.Details}");
anchor

Properties

PropertyTypeDescription
EventTypeTayraAuditEventTypeThe type of operation that occurred (required)
TimestampDateTimeOffsetWhen the event occurred (defaults to UtcNow)
KeyIdstring?The encryption key ID involved, if applicable
SubjectIdstring?The data subject identifier, if applicable
EntityTypestring?The .NET type name of the entity being processed
FieldCountint?The number of PII fields encrypted or decrypted
Detailsstring?Free-text additional details

Event Types

Tayra emits 14 distinct audit event types covering the full lifecycle of data protection operations:

cs
// All audit event types emitted by Tayra:
var allEventTypes = Enum.GetValues<TayraAuditEventType>();
foreach (var eventType in allEventTypes)
{
    Console.WriteLine($"  {eventType}");
}
// Output:
//   KeyCreated
//   KeyAccessed
//   KeyDeleted
//   BulkKeysDeleted
//   DataEncrypted
//   DataDecrypted
//   CryptoShreddingDetected
//   KeyExpired
//   DataSubjectAccessExported
//   DataSubjectPortableExported
//   BreachAssessed
//   BreachNotificationGenerated
//   DataMigrationCompleted
//   DataMigrationVerified
anchor

Complete Event Type Reference

Event TypeEmitted ByDescription
KeyCreatedDefaultCryptoEngine.GetOrCreateKeyAsync, RotateKeyAsyncA new encryption key was generated and stored
KeyAccessedDefaultCryptoEngine.GetOrCreateKeyAsync, GetKeyAsyncAn encryption key was retrieved (from cache or store)
KeyDeletedDefaultCryptoEngine.DeleteKeyAsyncA single encryption key was deleted (crypto-shredding)
BulkKeysDeletedDefaultCryptoEngine.DeleteAllKeysAsyncAll keys matching a prefix were deleted
DataEncryptedDefaultFieldEncrypter.EncryptAsyncPII fields on an entity were encrypted
DataDecryptedDefaultFieldEncrypter.DecryptAsyncPII fields on an entity were decrypted
CryptoShreddingDetectedDefaultFieldEncrypter (during decrypt)Decryption was attempted but the key was already shredded
KeyExpiredKeyRetentionBackgroundServiceA key was deleted by the retention policy
DataSubjectAccessExportedDefaultDataSubjectAccessServiceA GDPR Art. 15 data subject access report was generated
DataSubjectPortableExportedDefaultDataSubjectAccessServiceA GDPR Art. 20 data portability export was generated
BreachAssessedDefaultBreachNotificationServiceA data breach impact assessment was performed
BreachNotificationGeneratedDefaultBreachNotificationServiceA breach notification report was generated
DataMigrationCompletedData migration serviceA data migration batch was completed
DataMigrationVerifiedData migration serviceA data migration was verified

Default Logger Behavior

The DefaultTayraAuditLogger performs two actions for each event:

1. Structured Logging via ILogger

Events are logged at Information level with structured properties:

Tayra audit: DataEncrypted KeyId=patient-abc123 SubjectId=abc123 EntityType=PatientRecord FieldCount=3 Details=null

This integrates with any ILogger provider (Console, Serilog, Application Insights, etc.) and supports structured log queries.

2. OpenTelemetry ActivityEvents

If there is a current Activity (distributed trace), the logger adds an ActivityEvent named "tayra.audit" with tags:

TagDescription
tayra.audit.event_typeThe event type as a string
tayra.audit.key_idThe key ID (if present)
tayra.audit.subject_idThe subject ID (if present)
tayra.audit.entity_typeThe entity type (if present)
tayra.audit.field_countThe field count (if present)

These appear as span events in your distributed tracing backend (Jaeger, Zipkin, Grafana Tempo), giving you a unified view of audit events within request traces.

Compliance Storage

The default logger writes to ILogger, which is typically ephemeral (console, rolling files). For GDPR compliance, configure a persistent log sink (e.g., a database, immutable object store, or compliance-specific platform like Splunk or Azure Monitor) and set an appropriate retention period for audit records.

See Also