Skip to content

NServiceBus

Tayra integrates with NServiceBus through pipeline behaviors that automatically encrypt and decrypt [PersonalData] fields in messages flowing through the endpoint. It also provides a built-in ErasePersonalDataHandler for GDPR erasure workflows. If your organization already uses NServiceBus, adding Tayra is a natural fit — both are commercial products designed for production .NET systems.

Prerequisites

Tayra.NServiceBus requires NServiceBus 9.x or later. NServiceBus 9 introduced the Microsoft dependency injection integration that Tayra relies on for resolving ITayra.

Install

shell
dotnet add package Tayra.NServiceBus
powershell
Install-Package Tayra.NServiceBus

Setup

Register Tayra core services and a key store in your DI container, then call UseTayra() on the endpoint pipeline configuration:

csharp
services.AddTayra(opts => opts.LicenseKey = licenseKey); // defaults to InMemoryKeyStore; use PostgreSQL, Vault, etc. in production

var endpointConfiguration = new EndpointConfiguration("MyEndpoint");
endpointConfiguration.UseTransport<LearningTransport>();

// Register both Tayra behaviors on the pipeline
endpointConfiguration.Pipeline.UseTayra();

Pipeline.UseTayra() does two things:

  1. Registers TayraIncomingBehavior in the incoming logical message pipeline stage
  2. Registers TayraOutgoingBehavior in the outgoing logical message pipeline stage

You can also configure options through the DI container:

csharp
services.ConfigureTayraNServiceBus(opts =>
{
    opts.EncryptOutbound = true;   // default
    opts.DecryptInbound = true;    // default
});

Prerequisites

Tayra core services must be registered via services.AddTayra() (optionally chaining a production key store) before the endpoint starts. The behaviors resolve ITayra from the NServiceBus dependency injection container at runtime.

Options

csharp
services.ConfigureTayraNServiceBus(opts =>
{
    opts.EncryptOutbound = true;   // default
    opts.DecryptInbound = true;    // default
});
PropertyTypeDefaultDescription
EncryptOutboundbooltrueEncrypt PII fields on outbound messages before they reach the transport
DecryptInboundbooltrueDecrypt PII fields on inbound messages before they reach the handler

Messages

Annotate your NServiceBus message classes with [DataSubjectId] and [PersonalData]:

csharp
public class CreateCustomerCommand : ICommand
{
    [DataSubjectId]
    public string CustomerId { get; set; }

    [PersonalData]
    public string Name { get; set; }

    [PersonalData]
    public string Email { get; set; }

    public string AccountType { get; set; } // Not encrypted
}

public class CustomerCreatedEvent : IEvent
{
    [DataSubjectId]
    public string CustomerId { get; set; }

    [PersonalData]
    public string Name { get; set; }

    [PersonalData]
    public string Email { get; set; }

    public DateTime CreatedAt { get; set; }
}

How the Behaviors Work

NServiceBus processes messages through a staged pipeline. Tayra registers two behaviors at the logical message level:

  • TayraIncomingBehavior -- Runs in the incoming logical message stage, before the message handler. Decrypts all [PersonalData] fields so your handler receives cleartext.
  • TayraOutgoingBehavior -- Runs in the outgoing logical message stage, before the message reaches the transport. Encrypts all [PersonalData] fields so personal data is protected in transit and at rest.

Your handlers work with plaintext values and do not need to know about encryption:

csharp
public class CreateCustomerHandler : IHandleMessages<CreateCustomerCommand>
{
    public async Task Handle(
        CreateCustomerCommand message,
        IMessageHandlerContext context)
    {
        // message.Name and message.Email are already decrypted
        var customer = await SaveCustomer(message);

        await context.Publish(new CustomerCreatedEvent
        {
            CustomerId = message.CustomerId,
            Name = message.Name,       // Will be encrypted by TayraOutgoingBehavior
            Email = message.Email,     // Will be encrypted by TayraOutgoingBehavior
            CreatedAt = DateTime.UtcNow,
        });
    }
}

Graceful Error Handling

If decryption or encryption fails (e.g., a key store is temporarily unavailable), the behavior logs a warning and allows the message to proceed. This prevents transient key store failures from blocking message processing. Failed messages still benefit from NServiceBus's built-in retry and error queue mechanisms.

Sagas

Saga data classes work the same way as regular messages. Annotate saga data properties with [PersonalData] and the behaviors handle encryption transparently:

csharp
public class OnboardingPolicy :
    Saga<OnboardingPolicyData>,
    IAmStartedByMessages<CreateCustomerCommand>
{
    public async Task Handle(
        CreateCustomerCommand message,
        IMessageHandlerContext context)
    {
        // message.Name is already decrypted
        Data.CustomerName = message.Name;
        Data.CustomerEmail = message.Email;
    }

    protected override void ConfigureHowToFindSaga(
        SagaPropertyMapper<OnboardingPolicyData> mapper)
    {
        mapper.MapSaga(s => s.CustomerId)
            .ToMessage<CreateCustomerCommand>(m => m.CustomerId);
    }
}

public class OnboardingPolicyData : ContainSagaData
{
    [DataSubjectId]
    public string CustomerId { get; set; }

    [PersonalData]
    public string CustomerName { get; set; }

    [PersonalData]
    public string CustomerEmail { get; set; }
}

Saga Persistence

When NServiceBus persists saga data to its storage backend, the TayraOutgoingBehavior has already encrypted the PII fields. This means saga data at rest contains encrypted values regardless of which saga persister you use (SQL, RavenDB, Azure Table Storage, etc.).

GDPR Erasure Handler

Tayra includes a built-in handler for GDPR Article 17 "right to erasure" workflows:

csharp
// Send the command through NServiceBus
await endpointInstance.SendLocal(
    new ErasePersonalDataCommand("cust-42", "GDPR Article 17 request"));

The built-in ErasePersonalDataHandler:

  1. Calls cryptoEngine.DeleteAllKeysAsync(dataSubjectId) to delete all encryption keys
  2. Logs the erasure with the data subject ID and reason
  3. Publishes a PersonalDataErasedEvent for downstream handlers

Erasure Event

The PersonalDataErasedEvent is published by the built-in handler and can be consumed by your own handlers for audit logging, notifications, or cascading operations:

csharp
public class AuditErasureHandler : IHandleMessages<PersonalDataErasedEvent>
{
    private readonly ILogger<AuditErasureHandler> _logger;

    public AuditErasureHandler(ILogger<AuditErasureHandler> logger)
    {
        _logger = logger;
    }

    public Task Handle(
        PersonalDataErasedEvent message,
        IMessageHandlerContext context)
    {
        _logger.LogInformation(
            "Personal data erased for {Subject} at {Time}. Reason: {Reason}",
            message.DataSubjectId, message.ErasedAt, message.Reason);

        return Task.CompletedTask;
    }
}

See Also