Licensing
A valid license key is required to use Tayra in all environments. Request a free trial key at tayra.dev or purchase a production license - see pricing for details.
Setting Your License Key
You do not have to wire the key into your code. When opts.LicenseKey is not set, Tayra looks for the key in these places, in order, and uses the first one it finds:
| Order | Source | Good for |
|---|---|---|
| 1 | opts.LicenseKey, set in code | An application that reads the key from a source of its own |
| 2 | The Tayra:LicenseKey configuration value | appsettings.json, user secrets, a vault configuration provider, or the Tayra__LicenseKey environment variable |
| 3 | The TAYRA_LICENSE_KEY environment variable | CI pipelines, containers, and TayraHost.Create outside a host |
| 4 | The per-user license file: ~/.tayra/license.key on macOS and Linux, %LOCALAPPDATA%\Tayra\license.key on Windows | Developer machines |
A key set in code always wins, including one set by a services.Configure<TayraOptions>(...) call made after AddTayra(). The configuration value is read only when the container has an IConfiguration, which every generic host and WebApplication does; TayraHost.Create searches the environment variable and the file. Wherever the key comes from, it is validated in exactly the same way, and a key is still required in every environment.
On a developer machine
Save the key to the per-user file once, and every Tayra application, test host, and sample on that machine picks it up, with nothing added to the repository:
# macOS and Linux
mkdir -p ~/.tayra
printf '%s' 'your-license-key' > ~/.tayra/license.key# Windows
New-Item -ItemType Directory -Force "$env:LOCALAPPDATA\Tayra" | Out-Null
Set-Content -NoNewline "$env:LOCALAPPDATA\Tayra\license.key" 'your-license-key'Leading and trailing whitespace, including a trailing newline, is ignored.
In CI and production
Set the TAYRA_LICENSE_KEY environment variable from your pipeline's secret store, or supply Tayra:LicenseKey through your configuration (for example Tayra__LicenseKey from a mounted secret, or a Key Vault configuration provider). One license covers every developer and server in your organization, so one organization-level secret is enough.
In code
If the key comes from somewhere Tayra does not search, set it on the options:
services.AddTayra(opts =>
{
// Only needed for a source Tayra does not search on its own. Tayra:LicenseKey,
// TAYRA_LICENSE_KEY, and the per-user license file are picked up without this.
opts.LicenseKey = configuration["Secrets:TayraLicense"];
});Without a genuine license key the host fails to start, and encrypting throws a TayraLicenseException. The error lists every place Tayra looked. Resolving ITayra from the DI container does not throw by itself; see Build and migration jobs.
Keep the key out of public source control
The key is a signed entitlement, not a credential: it unlocks no data and no key store. It is still licensed to your organization, so keep it out of public repositories. The per-user file, user secrets, and your secret store all do that.
Trial Licenses
Trial licenses are free, fully-functional signed keys with a time limit (typically 15 days). Request one at tayra.dev or by emailing hello@tayra.dev. Trial keys work identically to production keys during the trial - there are no artificial limitations, watermarks, or feature gates.
The one difference is at expiry: a trial key stops encrypting new data when its term ends (with no grace window - that is production-only). Decryption behaves exactly as it does for a production key: it is perpetual and unconditional, the host still starts, and anything you encrypted during the trial stays readable. Move to a production key before you need to encrypt again.
Production Licenses
A production key carries a single license term (the expiresAt date, typically 12 months, extended by renewal). The term governs three things, and it is deliberately asymmetric so that a lapsed license can never cause a data outage:
- Decryption is perpetual and unconditional. Data already encrypted with your keys keeps decrypting forever, before and after the term ends. There is no license check on the decryption path at all - not the clock, not the term, not the grace period, not the build's publish date, not the state of the key. No licensing condition can make your stored PII unreadable.
- Encryption of new data is term-bound with a 60-day grace. New encryption is allowed through the term end plus a grace window (60 days by default, or whatever the key encodes). After the grace elapses,
EncryptAsyncthrows aTayraLicenseExceptionrather than storing data unprotected - Tayra never silently writes cleartext. - Adopting newer versions is bound by the term end (no grace). A release published on or before your term end is licensed for encryption use, regardless of version, including across majors. A release published after the term end still decrypts your data, but will not encrypt new data until you renew and receive a key with a later term. Decryption is unaffected by the running build's date.
When a missing, tampered, malformed, or unverifiable key is supplied, the host fails to start - that is the only startup-time failure, and it applies to trial and production keys alike. A key that verifies always starts, however far past its term. See the License for the binding text.
Renewal never risks your data
Because decryption is never gated and the startup gate only fails on an inauthentic key, letting a license lapse degrades to "can't encrypt new data" and "can't adopt newer builds" - never "can't read stored data" and never "won't boot".
Validation
Tayra validates the license key entirely offline - no network calls, no phone-home, no telemetry. Your application can run in air-gapped environments, private networks, or anywhere without internet access. The signature is checked once at startup; the runtime encryption gate (term + grace, and the version limit) is re-evaluated on each encrypt call, so a grace window that lapses while a long-running process is up is observed. DecryptAsync performs no license check whatsoever - there is no code path in Tayra that can refuse a decryption for licensing reasons.
Build and migration jobs
Some jobs build your application's service container without ever running it: Marten and Wolverine code generation (codegen write) during an image build, or schema migration (db-apply) as a job before the pods start. They resolve everything your endpoints and handlers depend on, ITayra included, but never read or write personal data, and they often run where you deliberately do not ship the license key.
These jobs work without a key. Resolving ITayra, IFieldEncrypter, or a Marten store configured with UseTayra() never checks the license. The key is enforced where it matters:
| Moment | Without a genuine key |
|---|---|
| Resolving Tayra's services, building the Marten store | Works |
Starting the host (host.StartAsync(), app.Run()) | Fails with TayraLicenseException |
TayraHost.Create(...) | Fails with TayraLicenseException |
| Encrypting personal data | Throws TayraLicenseException; nothing is written in cleartext |
| Serializing a type with no personal data | Works |
| Resolving a Compliance service | Works |
| Calling a Compliance service | Throws TayraLicenseException |
So an application that actually runs still refuses to start without its key, exactly as before, and a job that should not have touched personal data fails loudly if it tries to write some.
The log follows the same rule, so a build log stays clean:
| Moment | Missing key | Key supplied but not valid |
|---|---|---|
| Resolving Tayra's services | Nothing at the default log level (one Debug line saying no key is configured and what will need one) | One Warning line with the reason |
| Starting the host, or the first encryption | Error, once per process, with the exception | Error, once per process, with the exception |
All of these are logged under the Tayra.Licensing.LicenseChecker category.
Compliance services follow the same rule: resolving ITayraCompliance or any of its services works without a key, and the edition is checked when one of them is used.
Back Up Your License Key
Store the key with your key store backups
Tayra will not start without a genuine key. The term does not matter, so an expired key starts and decrypts normally, but an absent or corrupted one stops the host before it can read anything. That makes the key a link in your recovery chain, and the only link Radarleaf issues.
The failure this prevents is mundane: a secret that does not mount after a redeploy, an environment variable dropped from a new environment, configuration that does not bind. Your key store is untouched, every DEK is intact, every byte of ciphertext is intact, and the application still will not start. The remedy is to restore a few hundred bytes of configuration, so keep a copy of the key wherever you keep your key store backups and your disaster-recovery runbook.
Two recovery paths, with different requirements:
| Path | Needs | Notes |
|---|---|---|
| Through Tayra | Key store, license key (any term state), Tayra packages | The normal route. An expired key is fine. |
| Without Tayra | Key store only | Decrypt straight from the wire format specification, which is published for exactly this reason. No license key, no Tayra assemblies. |
The second path exists so that losing the first is recoverable. It is the reason the format is documented and why reference decryptors are published: if the key is gone, the company is gone, or both, the data is still yours to read.
Editions
Tayra is available in two editions. Both editions fully support GDPR-compliant data protection - the Compliance edition adds reporting and tooling, not the protection itself.
| Edition | Package | What You Get |
|---|---|---|
| Essentials | Tayra.Core | Field-level encryption, crypto-shredding, key rotation, blind indexes, observability-grade audit logging, all framework integrations, CLI tool - everything needed to protect personal data and comply with GDPR |
| Compliance | Tayra.Core + Tayra.Compliance | Everything in Essentials, plus the regulator-facing artifact suite: PII data maps (Art. 30), data subject access exports (Art. 15/20), breach notification reports (Art. 33/34), formatted HTML compliance reports, ECDSA-signed reports, scheduled report runs with pluggable archive, persistent hash-chained audit trail, and the curated Grafana dashboard |
One license = one legal entity
A purchased Tayra license covers a single named legal entity (the "Licensee" identified on the license key or purchase record). It does not extend to parent companies, subsidiaries, sister companies, joint ventures, or other affiliates - even when they share common ownership or control. Each separate legal entity that uses Tayra in production must obtain its own license. See the License for the binding text.
TIP
The Essentials edition already provides the technical foundation for GDPR compliance. The Compliance edition automates the reporting that GDPR requires - the kind of work that would otherwise take weeks of custom development.
The Compliance reporting features are distributed in a separate Tayra.Compliance NuGet package. Install it alongside Tayra.Core:
dotnet add package Tayra.ComplianceUsing a Compliance service (e.g., ITayraCompliance) with an Essentials license throws a TayraLicenseException on the first call. Resolving it does not, so a container that is only built, never used, needs no key (see Build and migration jobs). TayraComplianceHost.Create(...) checks the edition immediately.
Pricing & Tiers
For license tiers, pricing, and eligibility details, see tayra.dev/#pricing.
To obtain a license key, contact hello@tayra.dev.
Validating Your License
You can check whether a license is valid by resolving the LicenseChecker from the DI container:
var checker = provider.GetRequiredService<LicenseChecker>();
if (!checker.IsAuthentic)
{
// Missing / tampered / malformed key: the host refuses to start on this.
}
if (!checker.IsLicensed)
{
// Authentic, but no longer usable for new encryption (an expired trial).
// The host still started, and decryption still works.
}
// For a production key, IsLicensed stays true even after the term lapses. To find out whether new
// encryption is still permitted, ask the runtime gate: it throws when the term + grace has elapsed
// or the running build is newer than the term.
checker.EnsureCanEncrypt();There is no EnsureCanDecrypt counterpart, deliberately. Decryption is not gated, so there is nothing to ask.
The one boundary worth knowing: IsAuthentic is checked when the host starts, so a missing or corrupted key stops reads by preventing the application from starting at all, not by refusing a decryption. The error says so, and points at restoring the key rather than at buying one. See Back Up Your License Key.
See Also
- Dependency Injection - Service registration
- Options - All configuration options
