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(MaskValue = "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 MaskValue 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 all four PII attributes: [PersonalData], [DataSubjectId], [DeepPersonalData], and [SerializedPersonalData]
  • 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