Skip to content

Getting Started ​

Tayra is a .NET library for protecting personal data at the field level. You annotate your model properties with attributes like [PersonalData] and [DataSubjectId], and Tayra handles encryption, decryption, and GDPR-compliant crypto-shredding automatically.

This guide walks you through a complete encrypt-decrypt-shred workflow in under five minutes.

Install Package ​

You only need one package to get started:

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

Tayra.Core includes a built-in in-memory key store that is used by default during development, so no additional packages are needed to start encrypting.

Key Stores

For production, use a secrets manager: Tayra.KeyStore.Vault, Tayra.KeyStore.AzureKeyVault, or Tayra.KeyStore.AwsParameterStore. For local development with persistent keys across restarts, use Tayra.KeyStore.PostgreSql. See Key Stores for details.

Define Your Model ​

Annotate your entity class with [DataSubjectId] to identify the data owner and [PersonalData] to mark fields that contain personal information:

cs
public class Customer
{
    [DataSubjectId]
    public Guid Id { get; set; }

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

    [PersonalData(ReplacementValue = "redacted@example.com")]
    public string Email { get; set; } = "";

    /// <summary>
    /// Not annotated - stored and retrieved as plaintext.
    /// </summary>
    public string AccountType { get; set; } = "";
}
anchor
  • [DataSubjectId] on Id tells Tayra to derive the encryption key from this property's value.
  • [PersonalData] on Name and Email marks them for encryption.
  • The ReplacementValue property on Email specifies the value returned after crypto-shredding. If omitted, an empty string is used for string fields.
  • AccountType has no attribute and is never touched by Tayra.

Alternative: Fluent Configuration ​

If you prefer not to use attributes - for example, when working with third-party types, shared DTOs, or when you want centralized configuration - you can use the fluent API instead:

cs
var fluentServices = new ServiceCollection();
fluentServices.AddTayra(opts =>
{
    opts.LicenseKey = licenseKey;
    opts.Entity<FluentCustomer>(e =>
    {
        e.DataSubjectId(c => c.CustomerId);
        e.PersonalData(c => c.Name);
        e.PersonalData(c => c.Email);
    });
});
anchor

When to use each approach

  • Attributes: Convenient when you own the model class and want self-documenting code.
  • Fluent API: Useful for third-party types, shared DTOs, or when you prefer centralized configuration.

See Fluent API for the complete builder reference.

Configure Services ​

Register Tayra in your dependency injection container with AddTayra(), set your license key, and chain a key store:

cs
var services = new ServiceCollection();

// Register Tayra with your license key (request a trial key at https://tayra.dev)
services.AddTayra(opts =>
{
    opts.LicenseKey = Environment.GetEnvironmentVariable("TAYRA_LICENSE_KEY")
                      ?? licenseKey; // fallback for samples
});

var provider = services.BuildServiceProvider();
var tayra = provider.GetRequiredService<ITayra>();
anchor

AddTayra() registers ITayra and all supporting services. Resolve ITayra from the container - it's the single entry point for encryption, decryption, crypto-shredding, key rotation, and blind index queries. A valid license key is required - request a free trial key at tayra.dev. By default, Tayra uses the built-in InMemoryKeyStore (keys lost on restart). For local development with persistent keys, chain .UseSqliteKeyStore(). For production, use a secrets manager.

Configure Services (without DI) ​

If you don't use dependency injection, create a standalone host with TayraHost.Create:

cs
// Simple - in-memory key store, no builder needed
using var simpleTayra = TayraHost.Create(opts =>
{
    opts.LicenseKey = licenseKey;
});

await simpleTayra.EncryptAsync(new FluentCustomer { CustomerId = "non-di-001", Name = "Test", Email = "test@example.com" });
anchor

For key stores, compliance, or other configuration, use the builder overload - it supports the same extension methods as the DI path:

cs
// With builder - same extension methods as the DI path
using var builderTayra = TayraHost.Create(
    opts => opts.LicenseKey = licenseKey,
    builder =>
    {
        builder.UseSqliteKeyStore();
        builder.AddCompliance(complianceOpts =>
        {
            complianceOpts.AddEntityType<FluentCustomer>();
        });
    });

await builderTayra.EncryptAsync(new FluentCustomer { CustomerId = "non-di-002", Name = "Builder Test", Email = "builder@example.com" });
anchor

Encrypt and Decrypt ​

Create an entity, encrypt it, and then decrypt it back:

cs
// Create a customer with personal data
var customer = new Customer
{
    Id = Guid.NewGuid(),
    Name = "Jane Doe",
    Email = "jane@example.com",
    AccountType = "Premium",
};

Console.WriteLine("=== Original ===");
Console.WriteLine($"  Name:  {customer.Name}");
Console.WriteLine($"  Email: {customer.Email}");
Console.WriteLine($"  Type:  {customer.AccountType}");

// Encrypt - [PersonalData] fields become ciphertext in-place
await tayra.EncryptAsync(customer);

Console.WriteLine("\n=== After Encryption ===");
Console.WriteLine($"  Name:  {customer.Name}");   // Base64 ciphertext
Console.WriteLine($"  Email: {customer.Email}");   // Base64 ciphertext
Console.WriteLine($"  Type:  {customer.AccountType}"); // Unchanged

// Decrypt - fields are restored to their original plaintext values
await tayra.DecryptAsync(customer);

Console.WriteLine("\n=== After Decryption ===");
Console.WriteLine($"  Name:  {customer.Name}");   // "Jane Doe"
Console.WriteLine($"  Email: {customer.Email}");   // "jane@example.com"
anchor

After EncryptAsync, the Name and Email properties contain Base64-encoded AES-256-GCM ciphertext. AccountType is untouched. After DecryptAsync, the original plaintext values are restored.

Crypto-Shred ​

Crypto-shredding is GDPR-compliant data erasure. Instead of finding and deleting every copy of a person's data, you delete their encryption key. All their encrypted data becomes permanently unreadable:

cs
// Crypto-shred: delete the encryption key for this data subject
await tayra.ShredAsync(customer.Id.ToString());

// Re-encrypt to simulate reading from a data store after shredding
customer.Name = "encrypted-blob";
customer.Email = "encrypted-blob";

// Decrypt after shredding - replacement values are returned
await tayra.DecryptAsync(customer);

Console.WriteLine("\n=== After Crypto-Shredding ===");
Console.WriteLine($"  Name:  \"{customer.Name}\"");   // "" (default replacement)
Console.WriteLine($"  Email: \"{customer.Email}\"");   // "redacted@example.com" (custom replacement)
Console.WriteLine($"  Type:  {customer.AccountType}"); // Unchanged
anchor

After the key is deleted, DecryptAsync returns replacement values instead of the original data:

  • Name becomes "" (the default replacement for strings)
  • Email becomes "redacted@example.com" (the custom replacement specified in the attribute)

This is irreversible. The original data cannot be recovered.

Next Steps ​

  • Attributes - Learn about the PII attributes: [PersonalData], [DataSubjectId], and [DeepPersonalData]
  • Configuration - Customize cache duration, key size, and licensing
  • Architecture - Understand the AES-256-GCM wire format and key management
  • Key Stores - Configure PostgreSQL, Vault, Azure Key Vault, or AWS for production
  • Installation - Full package reference and target framework details