Synergy.Platform.Marten.NumericRevisions 1.6.0

dotnet add package Synergy.Platform.Marten.NumericRevisions --version 1.6.0
                    
NuGet\Install-Package Synergy.Platform.Marten.NumericRevisions -Version 1.6.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Synergy.Platform.Marten.NumericRevisions" Version="1.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Synergy.Platform.Marten.NumericRevisions" Version="1.6.0" />
                    
Directory.Packages.props
<PackageReference Include="Synergy.Platform.Marten.NumericRevisions" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Synergy.Platform.Marten.NumericRevisions --version 1.6.0
                    
#r "nuget: Synergy.Platform.Marten.NumericRevisions, 1.6.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Synergy.Platform.Marten.NumericRevisions@1.6.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Synergy.Platform.Marten.NumericRevisions&version=1.6.0
                    
Install as a Cake Addin
#tool nuget:?package=Synergy.Platform.Marten.NumericRevisions&version=1.6.0
                    
Install as a Cake Tool

Synergy.Platform.Marten

Centralized Marten governance for Synergy services. The Marten version and every persistence convention come from these packages, and no developer can drift them by mistake.

Packages

Package Reference it when… Concurrency (mt_version)
Synergy.Platform.Marten.Uuid your service owns its schema and shares no document tables (e.g. Dynamic Domain Builder) uuid optimistic concurrency
Synergy.Platform.Marten.NumericRevisions your service is a shared event-sourced service (Auth, Admin, Organisation, Template, DMS, …) integer numeric revisions
Synergy.Platform.Marten.Core never referenced directly — it's the shared core both leaves depend on —

You reference one leaf. Marten (pinned), the serializer, schema-per-service, StreamIdentity, the analyzer, and the build guard all arrive transitively via Core. The concurrency standard is the package name — there is no runtime flag to set wrong.

Usage

// the only line in your .csproj:
//   <PackageReference Include="Synergy.Platform.Marten.NumericRevisions" Version="1.1.0" />
//   (or Synergy.Platform.Marten.Uuid for an isolated service)

builder.Services.AddPlatformMarten(o =>
{
    o.ConnectionString = builder.Configuration.GetConnectionString("Default")!;
    o.ServiceSchema    = "auth";               // your owned schema (never "public")
    o.AutoCreate       = AutoCreate.None;      // CreateOrUpdate only for throwaway dev DBs / runtime-schema services
    o.Customize        = opts => { /* non-governed extras: indexes, extra mappings, BC schemas */ };
});

Enum storage (⚠️ read if upgrading from 1.1.0/1.2.0)

EnumStorage defaults to Marten's own default, AsInteger — so adopting the package does not change how your existing service stores enums. Set it only if your data actually stores enums as strings:

o.EnumStorage = EnumStorage.AsString;   // only if your existing data stores enums as strings

1.1.0 / 1.2.0 forced AsString on every service that used the governed serializer (bug). A service whose data stores enums as integers had its on-disk shape flipped, breaking reads/queries. 1.3.0 fixes it: the default is now AsInteger (Marten's default), and the value is per-service.

Note: a service that supplies its own serializer via the Customize hook (e.g. Dynamic Domain Builder installs its own JsonNetSerializer) overrides the governed serializer completely, so this setting does not affect it — its enum storage is whatever its own serializer uses.

Enforcement (why drift is impossible)

Wall Mechanism Stops
Analyzer SYN001 (Core) compile error raw AddMarten(...)
Build target SYNB01 (Core, buildTransitive) build error a direct Marten PackageReference
Pinned MartenVersion (Core) + Central Package Management version can't float Marten version drift
nuget.config package-source-mapping + locked restore resolves only from the controlled feed pulling Marten from anywhere else
Package choice (.Uuid vs .NumericRevisions) reviewed in the .csproj the wrong concurrency model

Consumer setup files are in templates/ — copy to each service repo root.

Versions at a glance

Package version Marten Why you would move to it
1.6.0 8.38.1 Latest stable 8.x. Same dependency set as 8.37.4, and it carries the security fix.
1.5.1 8.37.4 The security release for CVE-2026-75513. Minimum safe version.
1.1.0 - 1.5.0 8.37.3 Vulnerable. NuGet resolves the lowest allowed version, so these still pull 8.37.3 no matter what is published.

Services on 1.1.0-1.5.0 are NOT fixed by a new release existing; each one has to raise its own reference. As of 2026-10-01 the consumers in this estate were: Dynamic.Domain.Builder on 1.5.1, Synergy.Base.Auth and Synergy.Base.Notification on 1.5.0, platform-licensing-api, Synergy.Base.Admin and Synergy.DynamicDomains on 1.4.0.

Note on 8.38.x: it is published on NuGet but has no GitHub tag or release notes, so its changes are not publicly documented. Its dependency set is identical to 8.37.4. Services that reflect into Marten internals (Dynamic.Domain.Builder does, for dynamic document/event registration) should smoke test on a non-production environment before adopting 1.6.0; a plain consumer using AddPlatformMarten only has nothing to verify beyond its own tests.

Security: Marten 8.37.4 (⚠️ upgrade to 1.5.1)

1.5.1 raises the pinned Marten from 8.37.3 to 8.37.4, the 8.x security release for CVE-2026-75513 (critical): SQL injection in Marten's LINQ provider through unescaped string literals. The main attack path is a Dictionary<,> indexer key used in a Where filter, e.g. Where(x => x.Attributes[key] == v) with a user-supplied key. It can bypass filters (including tenant scoping) and leak data. ContainsKey(key) (Newtonsoft serializer) and constant strings in Select(...) are fixed too.

Every earlier version of this package (1.1.0 through 1.5.0) allows Marten 8.37.3, and NuGet picks the lowest allowed version, so services get the vulnerable build unless they upgrade. 1.5.1 changes nothing else: Marten 8.37.4 is a patch release with the same API and the same dependency versions. Bump the package version and redeploy. No code changes are needed.

Advisory-lock ids (⚠️ read if upgrading from ≤ 1.4.0)

Postgres advisory locks are per-database, not per-schema — and Marten ships the same default lock ids for every store. On the Synergy shared databases that broke the async daemon's HotCold leader election platform-wide: every service asked for the same lock, ONE service won and ran its projections, and every other service's daemon idled forever ("another copy of me has it"). Writes landed in the event store, read models never updated — the "created X doesn't show in the list until a manual projection rebuild" bug.

1.5.0 fixes it as a governed default: the daemon lock id is derived from ServiceSchema with a stable hash (FNV-1a — deliberately not string.GetHashCode(), which is per-process randomized and would break failover between replicas). Each service gets its own lock automatically; replicas of the same service still share one, so HotCold failover is unchanged. The schema-migration lock (ApplyChangesLockId) is derived alongside it. Nothing to configure — upgrade the package and restart.

o.DaemonLockId = 4001;   // escape hatch only — e.g. two services forced onto one schema name;
                         // must then be unique per service sharing the database

The derivation assumes one schema = one service per database (the recommended model this package already enforces). Services still on ≤ 1.4.0 against a shared database keep racing for Marten's default lock — upgrade every cohabiting service.

Migrations (schema changes without runtime AutoCreate)

The package owns migration so no service hand-rolls it. The running app never creates schema (AutoCreate.None is the default); schema is applied as a deliberate step, run as the schema-owning database role.

1. Drift guard — fail fast instead of running on a half-migrated DB. Turn on in UAT/prod:

builder.Services.AddPlatformMarten(o =>
{
    o.ConnectionString      = builder.Configuration.GetConnectionString("Default")!;
    o.ServiceSchema         = "auth";
    o.AssertSchemaOnStartup = !builder.Environment.IsDevelopment();  // boot throws if DB is behind the code
});

AssertSchemaOnStartup asserts the database matches the configuration on boot and throws if it doesn't — it never creates or alters anything. This catches "the DB is missing the latest tables/ functions" before the service serves traffic, instead of a cryptic runtime failure.

2. Apply the schema — a migration job (run as the owning role). Resolve the store the package already registered and call the governed migrator:

using var host = Host.CreateApplicationBuilder(args).Build();   // wires AddPlatformMarten
var store = host.Services.GetRequiredService<IDocumentStore>();

await PlatformMartenMigrator.AssertMatchesAsync(store);          // CI gate: throws on drift
await PlatformMartenMigrator.ApplyAsync(store);                  // apply CREATE/ALTER (as owning role)
await PlatformMartenMigrator.WriteCreationScriptAsync(store, "schema.sql");  // or dump SQL for DBA/Flyway

3. Or use the built-in CLI. Wire the service host once with return await app.RunJasperFxCommands(args); and every service gets db-assert, db-apply, db-dump <file>, db-patch <file>.

Ownership note (PostgreSQL): Marten upgrades replace its functions with CREATE OR REPLACE FUNCTION, which requires being the owner of the object. Run migrations as the schema-owning role (or a superuser), or reassign ownership once: REASSIGN OWNED BY <old_role> TO <service_role>; + ALTER DEFAULT PRIVILEGES IN SCHEMA <svc> GRANT ALL ON TABLES, FUNCTIONS TO <service_role>; Otherwise you get ERROR: must be owner of function ….

Feed (nuget.org — public)

Published to the public NuGet gallery (https://api.nuget.org/v3/index.json).

  • Consuming (CI or local dev): no credentials needed — it's the default public feed. Just reference the leaf package; restore works everywhere with no token.
  • Read is open to everyone; publishing is locked to the holder of the nuget.org API key (the Synergy organization) — see below.

Build & publish

dotnet build
dotnet pack Synergy.Platform.Marten.slnx -c Release -o artifacts
# publish (API key never committed — run by a maintainer or a CI secret):
dotnet nuget push "artifacts/*.nupkg" --source "https://api.nuget.org/v3/index.json" --api-key <NUGET_ORG_API_KEY> --skip-duplicate

Versions are immutable once pushed. Only the API-key holder can publish, so the version line moves only by a deliberate maintainer action.

Layout

src/Synergy.Platform.Marten.Core/            shared config + Marten pin + buildTransitive guard
src/Synergy.Platform.Marten.Core.Analyzers/  Roslyn analyzer (SYN001)
src/Synergy.Platform.Marten.Uuid/            uuid leaf (AddPlatformMarten -> uuid)
src/Synergy.Platform.Marten.NumericRevisions/ integer leaf (AddPlatformMarten -> numeric revisions)
templates/                                   Directory.Packages.props + nuget.config for consumers
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.6.0 58 10/1/2026
1.5.1 226 9/22/2026
1.5.0 192 9/1/2026
1.4.0 930 7/6/2026
1.3.0 209 7/1/2026
1.1.0 178 6/25/2026