{"id":50851477,"url":"https://github.com/yesbert/Stratara","last_synced_at":"2026-07-02T01:01:22.459Z","repository":{"id":360917301,"uuid":"1252076814","full_name":"yesbert/Stratara","owner":"yesbert","description":"CQRS and Event Sourcing for .NET — with tamper-evident streams and tenant-aware encryption built in. Integrated mediator, outbox, sagas, projections, and identity across 24 lockstep-versioned NuGet packages. MIT-licensed.","archived":false,"fork":false,"pushed_at":"2026-06-22T16:26:05.000Z","size":1174,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-22T17:04:57.206Z","etag":null,"topics":["audit-trail","cqrs","csharp","domain-driven-design","dotnet","encryption","event-driven-architecture","event-sourcing","gdpr","mediator","multi-tenant","nuget","outbox-pattern","postgresql","rabbitmq","saga-pattern","tamper-evident"],"latest_commit_sha":null,"homepage":"https://docs.stratara.tech/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yesbert.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-28T07:00:36.000Z","updated_at":"2026-06-22T16:56:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/yesbert/Stratara","commit_stats":null,"previous_names":["yesbert/stratara"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/yesbert/Stratara","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yesbert%2FStratara","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yesbert%2FStratara/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yesbert%2FStratara/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yesbert%2FStratara/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yesbert","download_url":"https://codeload.github.com/yesbert/Stratara/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yesbert%2FStratara/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35028642,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audit-trail","cqrs","csharp","domain-driven-design","dotnet","encryption","event-driven-architecture","event-sourcing","gdpr","mediator","multi-tenant","nuget","outbox-pattern","postgresql","rabbitmq","saga-pattern","tamper-evident"],"created_at":"2026-06-14T14:00:34.407Z","updated_at":"2026-07-02T01:01:22.444Z","avatar_url":"https://github.com/yesbert.png","language":"C#","funding_links":[],"categories":["Libraries/Frameworks"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"docs/assets/logo.png\" alt=\"Stratara\" width=\"240\"\u003e\n\n# Stratara\n\n**CQRS and Event Sourcing for .NET — with tamper-evident streams and tenant-aware encryption built in.**\n\n[![CI](https://github.com/yesbert/Stratara/actions/workflows/ci.yml/badge.svg)](https://github.com/yesbert/Stratara/actions/workflows/ci.yml) [![NuGet](https://img.shields.io/nuget/v/Stratara.Mediator?logo=nuget\u0026label=NuGet)](https://www.nuget.org/packages?q=Stratara) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Docs](https://img.shields.io/badge/docs-stratara.tech-2ea44f.svg)](https://docs.stratara.tech) [![.NET 10](https://img.shields.io/badge/.NET-10-512BD4.svg?logo=dotnet)](https://dotnet.microsoft.com/) [![GDPR Art. 17 — crypto-shredding](https://img.shields.io/badge/GDPR_Art._17-crypto--shredding-006400?logo=gnuprivacyguard\u0026logoColor=white)](https://docs.stratara.tech/concepts/tenant-aware-encryption.html)\n\n\u003c/div\u003e\n\n---\n\nStratara is the integrated CQRS, Event Sourcing, and audit stack you'd otherwise compose yourself from three or four libraries. Mediator, outbox, event store, sagas, projections, and identity — all wired together, lockstep-versioned across 24 NuGet packages for .NET 10. Opt in à la carte.\n\n\u003e **License:** Stratara ships under the **MIT License** ([LICENSE](LICENSE)) — OSI-approved open source, free for any use including commercial.\n\n## Why Stratara\n\n🔒 **Tamper-Evident by Design** — Every event stream is hash-chained. Manipulate a row directly in Postgres, and the next background-worker pass raises `EventStreamCorrupted` at the exact sequence number where the chain breaks. Audit-grade integrity, not a \"trust the DBA\" promise. ([Concept](https://docs.stratara.tech/concepts/tamper-evident-streams.html) · [Hero Sample](samples/Stratara.Sample.TamperProof))\n\n🛡️ **Tenant-Aware Encryption** — `[EncryptData]` fields are sealed with AES-GCM and an authentication tag bound to the tenant id as Associated Data. A row leaked from one tenant cannot be decrypted in another tenant's session — *even with the correct master key*. The tenant binding is cryptographic, not a `WHERE tenant_id = …` you hope nobody forgets. ([Concept](https://docs.stratara.tech/concepts/tenant-aware-encryption.html) · [Hero Sample](samples/Stratara.Sample.Encryption))\n\n⚖️ **GDPR Article 17 by Construction** — Append-only event sourcing and the right to erasure are natural enemies: you cannot delete an immutable event. Stratara's answer is **crypto-shredding** — each subject's data is encrypted under a destroyable per-scope key, and a single `EraseScopeAsync` call shreds that key so every copy — events, snapshots, replicas, *and backup tapes you can't even reach* — becomes undecryptable noise. Erasure without rewriting history, provable on a 30-day regulatory clock. The same per-subject-key model underwrites SOC 2 / ISO 27001 key-lifecycle controls and HIPAA per-patient separation. ([Concept](https://docs.stratara.tech/concepts/tenant-aware-encryption.html))\n\n🚦 **Tenant Isolation at the Door** — Beyond field-level encryption: a request that names a tenant other than the caller's is rejected at the *mediator entrance*, before your handler runs — opt-in via the `ITenantScopedRequest` marker. A strict mode routes every privileged cross-tenant operation (a platform admin acting on another tenant's data) through one auditable `ICrossTenantAuthorizer` that denies by default. The command-/query-side complement to the database tenant filters, so isolation isn't a `WHERE tenant_id = …` each handler has to remember. ([Guide](https://docs.stratara.tech/guides/enforce-tenant-isolation.html))\n\n🧩 **Integrated, not Assembled** — Mediator + Outbox + Event Store + Sagas + Projections + Identity, lockstep-versioned across 24 packages. One `\u003cVersionPrefix\u003e` bump moves everything together. No multi-library composition tax, no version-skew puzzles, no integration tests to prove your bus and your event store still see eye-to-eye.\n\n⚡ **Fast by Default, Scales Horizontally** — Reflection-free hot paths: commands, event replay, and projection dispatch run through compiled expressions, not `MethodInfo.Invoke`. Projections are push-driven — subscribed to the event bus, never polling a table. And every stream maps to one of 4096 deterministic buckets, so command, projection, and saga workers scale out as competing consumers across N nodes on RabbitMQ or Azure Service Bus. The measured numbers — replay throughput, reflection-free property access, constant-memory rebuilds — are in [Performance \u0026 horizontal scale](#performance--horizontal-scale) just below.\n\n## Performance \u0026 horizontal scale\n\nNumbers, not adjectives — measured with [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) on a **fanless MacBook Air M4** (Apple M4, .NET 10, Arm64 RyuJIT). Passively-cooled *laptop* hardware, so read these as conservative ratios, not a tuned server's ceiling. Re-run them yourself: `dotnet run -c Release --project tests/Stratara.Benchmarks -- --filter '*'`.\n\n**Replay stays cheap — and flat on memory.** In-memory aggregate replay through the compiled-expression apply-dispatch (excludes the database read a real rehydration adds):\n\n| Events replayed | Time | Allocated |\n|---|---:|---:|\n| 10,000 | 0.11 ms | 64 B |\n| 100,000 | 1.13 ms | 64 B |\n| 1,000,000 | 11.6 ms | **64 B** |\n\n~11 ns per event — and the allocation stays **constant at 64 bytes** no matter how long the stream is. A million-event aggregate rebuilds without producing garbage.\n\n**Reflection-free hot paths.** Commands, event-apply, projections, and sagas dispatch through compiled `Expression` delegates, never `MethodInfo.Invoke`. The same machinery drives property access:\n\n| Property access | Reflection | Compiled delegate | Speedup |\n|---|---:|---:|---:|\n| Write | 6.04 ns | 0.47 ns | **~13× faster** |\n| Read | 3.86 ns | 0.62 ns | **~6× faster** |\n\nBoth allocation-free. Source-generated logging adds **~0.5 ns / 0 B** when its level is off (classic interpolated logging allocates regardless); tamper-evident chain hashing runs **sub-microsecond per event** (hardware-accelerated SHA-256). Full methodology and the honest caveats live in [Performance \u0026 Scaling](https://docs.stratara.tech/concepts/performance-and-scaling.html).\n\n**Scales horizontally.** Commands flow through a message bus to *competing-consumer* workers — you add nodes, not bigger machines. Stream ids partition deterministically across 4096 buckets (single-writer per aggregate, full parallelism across aggregates), and projections are pushed from the event bus, never polled.\n\n```mermaid\nflowchart LR\n    A1[API / host 1] --\u003e BUS\n    A2[API / host 2] --\u003e BUS\n    A3[API / host N] --\u003e BUS\n    BUS{{\"Message Bus\u003cbr/\u003eRabbitMQ / Azure Service Bus\"}}\n    BUS --\u003e|competing consumers| C1[Command Worker 1]\n    BUS --\u003e C2[Command Worker 2]\n    BUS --\u003e C3[Command Worker N]\n    C1 --\u003e ES\n    C2 --\u003e ES\n    C3 --\u003e ES\n    ES[(\"Event Store\u003cbr/\u003e4096 stream buckets\")] --\u003e|pushed event bundles| P1[Projection / Saga Worker 1]\n    ES --\u003e P2[Projection / Saga Worker N]\n    P1 --\u003e RM[(Read Models)]\n    P2 --\u003e RM\n```\n\n## Why we share this\n\nStratara is the integrated CQRS / Event Sourcing / audit stack we built for our own products — the wiring that production event-sourced apps tend to write from scratch, plus the tamper-evident and tenant-aware-encryption properties we wanted as a *default*, not as an enterprise-tier add-on.\n\nWe're publishing it because the .NET ecosystem deserves these primitives without the composition tax of stitching together Marten + Wolverine + MassTransit + your own crypto layer. Compliance-relevant integrity (GDPR Article 17 via crypto-shredding, SOC 2 audit-trail, HIPAA data integrity) should not be locked behind a license tier — it should be how the storage layer works by default.\n\nThat's why Stratara ships under the **MIT License** — true open source, free for any use including commercial, with no competition clause and no time-delay. Take what you need, fork it, build on it.\n\n## Documentation\n\nFull docs, conceptual overview, getting-started walkthrough, guides, samples and the auto-generated API reference live at **[docs.stratara.tech](https://docs.stratara.tech)**.\n\n**Using an AI assistant?** Stratara ships an [`llms.txt`](llms.txt) at the repo root — a machine-readable index of the core interfaces, routing conventions, tier rules, and package map, following the [llms.txt convention](https://llmstxt.org). Point your assistant at it, or connect any MCP-capable client to `gitmcp.io/yesbert/Stratara` (which reads `llms.txt` first) so it grounds on current Stratara APIs instead of stale training data.\n\n## Install\n\n```bash\n# Minimum: in-process mediator + pipeline behaviors\ndotnet add package Stratara.Mediator\n\n# Event-sourced apps with the full stack\ndotnet add package Stratara.EventSourcing.EntityFrameworkCore\ndotnet add package Stratara.EventSourcing.WorkerDefaults\ndotnet add package Stratara.Outbox.RabbitMQ\n```\n\nHello-mediator in five lines:\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddMediator();\nbuilder.Services.AddCommandHandlersFromAssemblyContaining\u003cProgram\u003e();\nvar app = builder.Build();\nawait app.Services.GetRequiredService\u003cIMediator\u003e().HandleAsync(new MyCommand(), CancellationToken.None);\n```\n\n## Package map\n\nLockstep-versioned NuGet family — every package in the table below ships at the same `\u003cVersionPrefix\u003e`, bumped together (Microsoft.Extensions.* convention).\n\n| Tier | Package | Purpose |\n|---|---|---|\n| A | `Stratara.Abstractions` | Contract interfaces + POCO records (no implementation) |\n| A | `Stratara.Contracts` | Wire-level POCO contracts |\n| A | `Stratara.Diagnostics` | `ActivitySource` / `Meter` / log-event-ID schema |\n| A | `Stratara.Resilience` | Polly named pipelines |\n| B | `Stratara.Sessions` | Actor / Subject session model + ASP.NET middleware |\n| B | `Stratara.Mediator` | In-process mediator + pipeline behaviors |\n| B | `Stratara.Domain` | Tenant aggregate + lifecycle events |\n| B | `Stratara.Shared` | Umbrella re-export of A/B abstractions + source-generated logger extensions |\n| B | `Stratara.ServiceDefaults` | OpenTelemetry + Serilog defaults |\n| C | `Stratara.EventSourcing.EntityFrameworkCore` | Write / read / identity stores on PostgreSQL |\n| C | `Stratara.EventSourcing.Pipeline.CommandAudit` | Command-audit pipeline behavior |\n| C | `Stratara.Validation` | Vendor-neutral `IValidator\u003cT\u003e` + validation pipeline behavior |\n| C | `Stratara.EventSourcing.WorkerDefaults` | Worker-host wiring composites |\n| C | `Stratara.Projections` | Projection runtime |\n| C | `Stratara.Sagas` | Saga runtime |\n| C | `Stratara.Security` | Key store (KEK-wrapped versioned DEKs) + AES-GCM envelope encryption |\n| C | `Stratara.Outbox.RabbitMQ` | Outbox + RabbitMQ-backed `IMessageBus` |\n| C | `Stratara.Outbox.AzureServiceBus` | Outbox + Azure Service Bus-backed `IMessageBus` |\n| C | `Stratara.Infrastructure` | Cross-cutting infrastructure glue |\n| C | `Stratara.Identity.Core` | Channel-agnostic identity primitives |\n| C | `Stratara.Identity.AspNetCore` | Channel-agnostic ASP.NET Core identity wiring (sign-in manager wrapper + i18n + email-sender stub) |\n| C | `Stratara.ServiceDefaults.AspNetCore` | ASP.NET health checks + request OpenTelemetry |\n| — | `Stratara.Testing` | Test doubles (in-memory key store / message bus / session) + given/when/then aggregate harness — reference from test projects only |\n| — | `Stratara.Testing.EntityFrameworkCore` | Run the real event-sourcing write stack on in-memory SQLite (`EventStoreTestHost`) — reference from test projects only |\n\n**Tier order**: a Tier-N package may only reference Tier-(≤N). Tier-A has no inbound dependencies from B or C.\n\n## Quick start\n\nThe fastest path is to run the **samples** under [`samples/`](samples/) — each is self-contained, runs in under a second, and reads top-to-bottom in 5–20 minutes.\n\n### 🌟 Hero Samples — see what makes Stratara different\n\n| Sample | What it proves |\n|---|---|\n| [`Stratara.Sample.TamperProof`](samples/Stratara.Sample.TamperProof) | Hash-chained event streams catch direct-DB tampering at the next verifier pass |\n| [`Stratara.Sample.Encryption`](samples/Stratara.Sample.Encryption) | Tenant-bound AAD makes cross-tenant decryption cryptographically impossible |\n\n### 📚 Learning Path — five samples on a shared bank-account domain\n\n| # | Sample | Concept |\n|---|---|---|\n| 1 | [`Stratara.Sample.CqrsBasics`](samples/Stratara.Sample.CqrsBasics) | `IMediator` + `ICommand` / `IQuery` + handler discovery |\n| 2 | [`Stratara.Sample.EventSourced`](samples/Stratara.Sample.EventSourced) | Event-sourced aggregate + read-side projection |\n| 3 | [`Stratara.Sample.OutboxWorker`](samples/Stratara.Sample.OutboxWorker) | Outbox + message bus + background worker |\n| 4 | [`Stratara.Sample.MoneyTransferSaga`](samples/Stratara.Sample.MoneyTransferSaga) | Saga / process manager |\n| 5 | [`Stratara.Sample.AspNetCoreApi`](samples/Stratara.Sample.AspNetCoreApi) | HTTP minimal-API → mediator wiring |\n\n```bash\ndotnet run --project samples/Stratara.Sample.TamperProof\n```\n\nEach sample has a step-by-step walkthrough under [docs.stratara.tech/samples](https://docs.stratara.tech/samples/).\n\n## Build from source\n\nRequires the **.NET 10 SDK** — `global.json` pins the version; `dotnet --version` should report `10.0.x`.\n\n```bash\n# Build the publish solution filter (every packable csproj + tests)\ndotnet build Stratara.Publish.slnf -c Release\n\n# Run the test suite (xUnit v3 with Microsoft Testing Platform)\ndotnet test\n```\n\n## Versioning\n\nLockstep across the whole family — one `\u003cVersionPrefix\u003e` in `Directory.Build.props` controls every package. Tag-driven builds (`v*`) publish stable versions; main-branch pushes publish `{VersionPrefix}-preview.{BuildId}` pre-releases. SemVer applies — see [`CHANGELOG.md`](CHANGELOG.md) for per-release notes.\n\n## License\n\n**MIT** — see [`LICENSE`](LICENSE).\n\nStratara is OSI-approved open source. You may use, copy, modify, and distribute it for any purpose — including commercial — subject only to the MIT License's attribution requirement.\n\n## Contributing\n\nIssues, questions, and ideas are genuinely welcome — they shape where Stratara goes next:\n\n- **Bug reports** — [open an issue](https://github.com/yesbert/Stratara/issues/new/choose) with the bug template.\n- **Feature ideas \u0026 questions** — open an issue with the question template (check [docs.stratara.tech](https://docs.stratara.tech) first).\n- **Security issues** — see [`SECURITY.md`](SECURITY.md); please don't file a public issue.\n\nA note on the workflow: this GitHub repository is a one-way mirror of an internal Azure DevOps source-of-truth, force-pushed as a squashed commit per release. We can't merge pull requests here — they'd be overwritten on the next sync — so please open an issue instead. Good ideas make their way in through the internal repo, with credit.\n\nFull details on the contribution model: [`CONTRIBUTING.md`](CONTRIBUTING.md). Community standards: [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md). Getting help: [`SUPPORT.md`](SUPPORT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyesbert%2FStratara","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyesbert%2FStratara","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyesbert%2FStratara/lists"}