Lumarin.Notify.EntityFrameworkCore.PostgreSQL 0.8.0-preview.158

This is a prerelease version of Lumarin.Notify.EntityFrameworkCore.PostgreSQL.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Lumarin.Notify.EntityFrameworkCore.PostgreSQL --version 0.8.0-preview.158
                    
NuGet\Install-Package Lumarin.Notify.EntityFrameworkCore.PostgreSQL -Version 0.8.0-preview.158
                    
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="Lumarin.Notify.EntityFrameworkCore.PostgreSQL" Version="0.8.0-preview.158" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lumarin.Notify.EntityFrameworkCore.PostgreSQL" Version="0.8.0-preview.158" />
                    
Directory.Packages.props
<PackageReference Include="Lumarin.Notify.EntityFrameworkCore.PostgreSQL" />
                    
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 Lumarin.Notify.EntityFrameworkCore.PostgreSQL --version 0.8.0-preview.158
                    
#r "nuget: Lumarin.Notify.EntityFrameworkCore.PostgreSQL, 0.8.0-preview.158"
                    
#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 Lumarin.Notify.EntityFrameworkCore.PostgreSQL@0.8.0-preview.158
                    
#: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=Lumarin.Notify.EntityFrameworkCore.PostgreSQL&version=0.8.0-preview.158&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Lumarin.Notify.EntityFrameworkCore.PostgreSQL&version=0.8.0-preview.158&prerelease
                    
Install as a Cake Tool

Lumarin.Notify

Multi-channel notification infrastructure for .NET. Reliable delivery across in-app feeds, realtime SignalR push, email, SMS, and mobile push — from a single unified API, with durable delivery tracking, templating, retry, rate limiting, and user preference enforcement built in.

var result = await hub.SendAsync(new NotificationRequest(
    ScopeType: "user",
    ScopeId:   "user-123",
    Category:  "order.shipped",
    Content: new NotificationContent("Order shipped", "Your order #456 is on the way!"),
    Recipients: new NotificationRecipients(
        Identities: ["user-123"],
        Channels:   [NotificationChannel.InApp, NotificationChannel.Email]),
    Options: new NotificationOptions(
        IdempotencyKey: "order-456-shipped",
        Priority:       NotificationPriority.High)));

Choose your packages

What you need Packages to add
Send notifications and in-app feed Lumarin.Notify
Durable delivery tracking and EF Core persistence + Lumarin.Notify.EntityFrameworkCore
HTTP endpoints for feed, preferences, devices + AspNetCore
Template rendering (SendFromTemplateAsync) + Templates
Ordered fallback routing and simulation + PolicyRouting
Quiet-hours deferral for policy routing (durable only) + PolicyRouting + Scheduling + Scheduling.EntityFrameworkCore + PolicyRouting.Scheduling
One-time scheduled notifications (preview) + Scheduling + Scheduling.EntityFrameworkCore
Realtime WebSocket push + Channels.SignalR
Email delivery + Channels.Email + one email adapter package
Push notifications (FCM / OneSignal) + Channels.Push + one push adapter package
SMS (Twilio) + Channels.Sms + one SMS adapter package

Installation

Add the package references that match your profile:

  • Core runtime: Lumarin.Notify
  • Durable persistence profile: Lumarin.Notify.EntityFrameworkCore
  • Hosted HTTP endpoints for feed, preferences, devices, and template admin: Lumarin.Notify.AspNetCore
  • Template rendering for SendFromTemplateAsync(...): Lumarin.Notify.Templates
  • Ordered fallback routing: Lumarin.Notify.PolicyRouting
  • Durable quiet-hours deferral bridge for policy routing: Lumarin.Notify.PolicyRouting.Scheduling
  • One-time scheduling preview: Lumarin.Notify.Scheduling and Lumarin.Notify.Scheduling.EntityFrameworkCore
  • SignalR channel: Lumarin.Notify.Channels.SignalR
  • Email channel: Lumarin.Notify.Channels.Email plus one provider adapter: Lumarin.Notify.Channels.Email.Smtp, Lumarin.Notify.Channels.Email.Ses, or Lumarin.Notify.Channels.Email.SendGrid
  • Push channel: Lumarin.Notify.Channels.Push plus one provider adapter: Lumarin.Notify.Channels.Push.Fcm or Lumarin.Notify.Channels.Push.OneSignal
  • SMS channel: Lumarin.Notify.Channels.Sms plus one provider adapter: Lumarin.Notify.Channels.Sms.Twilio

Provider credential requirements

Channel package Required credentials
Channels.Email + .Email.Smtp / .Email.Ses / .Email.SendGrid SMTP host/port/credentials, AWS SES credentials, or SendGrid API key
Channels.Push + .Push.Fcm / .Push.OneSignal Firebase service account (FCM) or OneSignal App ID + API key
Channels.Sms + .Sms.Twilio Twilio Account SID, Auth Token, and sender number
Channels.SignalR None — runs in-process; Redis or Azure SignalR Service for multi-node

Channel registration fails fast at startup if the matching provider adapter package or required credentials are missing.

The root builder now owns the default provider-registration story:

builder.Services.AddLumarinNotify(options =>
{
    options.UsePostgreSql(builder.Configuration.GetConnectionString("LumarinNotify")!);

    options.EnableEmail(email =>
    {
        email.FromAddress = "noreply@example.com";
        email.FromName = "Lumarin.Notify";
    });
    options.UseSendGrid(builder.Configuration["LumarinNotify:Channels:Email:SendGrid:ApiKey"]!);
});

Prerequisites

  • .NET 10 SDK
  • PostgreSQL or SQL Server for production; EF Core InMemory for dev/tests

Architecture and governance

For repository rules and contribution guidance:

  • human contributors: CONTRIBUTING.md
  • AI coding agents: AGENTS.md
  • canonical governance docs: docs/architecture/00-README.md

The durable source of truth for architecture guardrails, package boundaries, composition modes, docs governance, and review checklists lives under docs/architecture/.

Stability and support

Lumarin.Notify uses five repo-wide tiers: Stable, Supported optional, Preview, Internal, and Sample-only.

  • Stable: core runtime packages, durable EF packages, production channel families, the library-owned /api/notify/* contract, and Lumarin.Notify.AspNetCore.OpenApiClient
  • Supported optional: outbox, admin, policy-routing, telemetry, claims, and bridge packages when explicitly adopted
  • Preview: scheduling, webhook delivery, and reserved scaffold adapters
  • Sample-only: reference host, runnable samples, and showcase tooling remain adoption-lane or evaluation surfaces, not reusable package contracts
  • Internal: repo-local generators and helper settings remain repository tooling rather than consumer dependencies

Consumer-facing guidance lives in docs-site/docs/support-matrix.md, docs-site/docs/package-map.md, docs-site/docs/stability-tiers.md, and docs-site/docs/adoption-checklist.md. Canonical governance for support tiers and compatibility policy lives in docs/architecture/15-STABILITY-AND-SUPPORT-TIERS.md and docs/architecture/16-COMPATIBILITY-AND-RELEASE-POLICY.md.

Integration, scale, and extensibility

Consumer-facing guidance for application shape, tuning, and supported customization lives in docs-site/docs/integration-patterns.md, docs-site/docs/scale-and-performance.md, and docs-site/docs/extensibility.md.

Contributor workflow guidance lives in docs/contributor-workflows.md.

Receiver or user-plane adoption

Use the published receiver guide when you are deciding whether your frontend should call the library-owned hosted routes directly or a host-owned facade.

  • Direct hosted contract: docs-site/docs/receiver-integration.md
  • Hosted route and actor-resolution details: docs-site/docs/aspnetcore-hosting.md
  • Host-owned /api/me/* recipe: docs-site/docs/recipes/receiver-host-facade.md

Receiver defaults stay explicit:

  • feed is the canonical persisted receiver state
  • preferences and devices are current-actor surfaces
  • raw push tokens stay write-only and the generic hosted contract does not define GET /api/notify/devices
  • SignalR is optional realtime fan-out and should be treated as a refresh hint rather than canonical feed state

Adoption profiles

Profile When to use it Core registration
Simple Operational without EF Core using in-memory defaults, no hosted delivery runtime, consumer-owned orchestration and dispatch AddLumarinNotify(...) with Delivery.EnableBackgroundWorkers = false and no durable storage registration
Durable Notifications and deliveries stored in EF Core, direct-delivery runtime when background workers are enabled, no outbox by default AddLumarinNotify(...) + UsePostgreSql(...) or UseSqlServer(...) inside the builder root
Outbox Explicit durable outbox mode with mandatory startup validation and outbox runtime selection when background workers are enabled Durable profile + EnableOutbox(...) inside AddLumarinNotify(...)

Simple setup

// Program.cs
using Lumarin.Notify.DependencyInjection;

builder.Services.AddLumarinNotify(options =>
{
    options.Delivery.EnableBackgroundWorkers = false;
    options.Delivery.MaxRetries = 3;
    options.Templates.Engine = "scriban";
    options.EnableTemplates();
});

Use this profile when you want the public notification services without EF Core. The library registers in-memory defaults for the required persistence seams and does not register a hosted delivery runtime in simple mode. Notifications and queued deliveries are created in-memory, and dispatch remains consumer-owned.

Durable setup

// Program.cs
using Lumarin.Notify.DependencyInjection;

builder.Services.AddLumarinNotify(options =>
{
    options.UsePostgreSql(builder.Configuration.GetConnectionString("LumarinNotify")!); // or UseSqlServer(...)
    options.Delivery.MaxRetries = 3;
    options.EnableTemplates();
    options.EnableEmail(email =>
    {
        email.FromAddress = "noreply@example.com";
        email.FromName = "Lumarin.Notify";
    });
    options.UseSendGrid(builder.Configuration["LumarinNotify:Channels:Email:SendGrid:ApiKey"]!);
});

var app = builder.Build();
app.Run();

This profile replaces the core in-memory defaults with EF Core repositories and keeps the direct-delivery runtime unless you explicitly opt into outbox mode. Durable registrations apply the active Lumarin.Notify migration stream at startup by default. Set Database.Migrations.ApplyOnStartup = false when a separate deploy or migrator step owns schema changes. When you need controlled orchestration, keep using the explicit migration helpers such as MigrateLumarinNotifyAsync() and MigrateLumarinNotifyAdminAsync().

Inject INotificationHub and send:

var result = await hub.SendAsync(new NotificationRequest(
    ScopeType: "user",
    ScopeId:   "user-123",
    Category:  "order.shipped",
    Content: new NotificationContent("Order shipped", "Your order #456 is on the way!"),
    Recipients: new NotificationRecipients(
        Identities: ["user-123"],
        Channels:   [NotificationChannel.InApp, NotificationChannel.Email]),
    Options: new NotificationOptions(
        IdempotencyKey: "order-456-shipped",
        Priority:       NotificationPriority.High)));

Outbox setup

using Lumarin.Notify.Outbox.DependencyInjection;

builder.Services.AddLumarinNotify(options =>
{
    options.UsePostgreSql(builder.Configuration.GetConnectionString("LumarinNotify")!);
    options.EnableDurableDefaults();
    options.EnableOutbox(outbox =>
    {
        outbox.BatchSize = 50;
        outbox.MaxRetries = 5;
    });
});

Outbox settings are configured through EnableOutbox(...) on the root builder or, for advanced direct composition, UseLumarinNotifyOutbox(...). When the standard durable runtime shape is enough, EnableDurableDefaults() fills in outbox and scheduling after the host explicitly chooses UsePostgreSql(...) or UseSqlServer(...). After EnableDurableDefaults(), hosts may still make one explicit EnableOutbox(...) or EnableScheduling(...) call to refine package-specific options without re-registering the durable profile. When background workers are enabled, outbox registration selects the outbox runtime profile instead of mutating existing hosted-service registrations. When background workers are disabled, the consumer owns outbox dispatch and retention orchestration explicitly. Mandatory outbox startup validation still runs even if optional startup validation diagnostics were disabled earlier in core options. Outbox EF persistence stays opt-in for runtime and migrations. Base SQL Server/PostgreSQL provider packages no longer pull the outbox EF package by default; generate outbox-aware migrations from the dedicated provider-specific Outbox migration assemblies or from an application-owned migration assembly that references Lumarin.Notify.Outbox.EntityFrameworkCore. Outbox-capable durable hosts also bootstrap the active migration stream at startup by default. Set Database.Migrations.ApplyOnStartup = false only when an external migrator owns the schema step. If you bootstrap schema creation at runtime, use the helper that matches the active profile:

// Durable profile
await app.Services.MigrateLumarinNotifyAsync();

// Outbox profile
await app.Services.MigrateLumarinNotifyOutboxAsync();

If the host also registers AddLumarinNotifyAdminEntityFrameworkCore(), use await app.Services.MigrateLumarinNotifyAdminAsync(); so the admin control-plane provider and policy schema is applied before enabling admin mutation or policy routes on an existing database.

Scheduling preview

Lumarin.Notify.Scheduling is an optional preview package for durable one-time scheduled notifications. It is notification-native, not a general scheduler, and it is not part of the minimal Release 0 validation path.

using Lumarin.Notify.Scheduling.DependencyInjection;
using Lumarin.Notify.Scheduling.EntityFrameworkCore.Extensions;

builder.Services.AddLumarinNotify(options =>
{
    options.UsePostgreSql(builder.Configuration.GetConnectionString("LumarinNotify")!);
    options.EnableScheduling(scheduling =>
    {
        scheduling.BatchSize = 100;
        scheduling.ClaimLeaseDuration = TimeSpan.FromMinutes(1);
        scheduling.MaxDispatchAttempts = 5;
    });
});

var app = builder.Build();
await app.Services.MigrateLumarinNotifySchedulingAsync();

var scheduling = app.Services.GetRequiredService<ISchedulingOperations>();
await scheduling.ScheduleAsync(new ScheduleNotificationRequest(
    new NotificationRequest(
        ScopeType: "user",
        ScopeId: "user-123",
        Category: "digest.morning",
        Content: new NotificationContent("Morning digest", "3 new jobs match your search."),
        Recipients: new NotificationRecipients(["user-123"])),
    ScheduleInput.ForDelay(TimeSpan.FromMinutes(30))));

EnableScheduling(...) inside AddLumarinNotify(...) is the primary activation path. When Lumarin.Notify.Scheduling.EntityFrameworkCore is referenced, the root builder wires scheduling persistence automatically. UseLumarinNotifyScheduling(...) plus AddLumarinNotifySchedulingEntityFrameworkCore() remains available for advanced direct composition. If you need recurrence, cron, workflows, dashboards, or general background jobs, use Hangfire or Quartz instead.

Policy routing

Lumarin.Notify.PolicyRouting is an optional package for ordered channel routing, priority-based route profiles, bounded delivery-path escalation, tenant overrides, bounded cost-aware ordering, structured traces, simulation, and failure-time continuation when a delivery step reaches a terminal failure.

using Lumarin.Notify.Models;
using Lumarin.Notify.PolicyRouting.DependencyInjection;
using Lumarin.Notify.PolicyRouting.Models;

builder.Services.UseLumarinNotifyPolicyRouting(options =>
{
    options.Policy.DefaultChannels.AddRange([
        NotificationChannel.Push,
        NotificationChannel.Email,
    ]);

    options.Policy.LowPriorityChannels.AddRange([
        NotificationChannel.InApp,
        NotificationChannel.Email,
    ]);

    options.Policy.HighPriorityEscalationChannels.AddRange([
        NotificationChannel.Email,
        NotificationChannel.Sms,
    ]);

    options.Policy.QuietHoursStrategy = PolicyRoutingQuietHoursStrategy.DeferWhenAllRoutesQuietHoursBlocked;
    options.Policy.CriticalBypassesQuietHours = true;
});

UseLumarinNotifyPolicyRouting(...) is the feature switch. It does not add a hosted runtime, it works with Simple, Durable, and Outbox profiles, and it keeps delivery on the normal Lumarin.Notify pipeline.

Release B Track 3 supports ordered channel routing, priority-based route profiles, bounded high/critical route widening, static cost hints, tenant overrides, structured execution traces, simulation, and continuation after terminal failure. Continuation depends on internal route-plan metadata; if that metadata is missing or unreadable, the runtime falls back to the normal dead-letter path. The base package stays channel-level and mode-neutral: no provider-level routing, timed workflow escalation, percentage rollout, or workflow/state-machine orchestration is included in this slice.

Policy-routing quiet-hours deferral bridge

Lumarin.Notify.PolicyRouting.Scheduling is the only package that turns policy-routing quiet-hours suppression into durable deferral.

using Lumarin.Notify.PolicyRouting.Scheduling.DependencyInjection;
using Lumarin.Notify.PolicyRouting.DependencyInjection;
using Lumarin.Notify.Scheduling.DependencyInjection;
using Lumarin.Notify.Scheduling.EntityFrameworkCore.Extensions;

builder.Services.AddLumarinNotify(options =>
{
    options.UsePostgreSql(builder.Configuration.GetConnectionString("LumarinNotify")!);
    options.EnableScheduling(scheduling =>
    {
        scheduling.BatchSize = 100;
        scheduling.ClaimLeaseDuration = TimeSpan.FromMinutes(1);
        scheduling.MaxDispatchAttempts = 5;
    });
});
builder.Services.UseLumarinNotifyPolicyRouting();
builder.Services.UseLumarinNotifyPolicyRoutingScheduling();

var app = builder.Build();
await app.Services.MigrateLumarinNotifySchedulingAsync();

This bridge is durable-only. It requires the durable or outbox profile plus active policy routing and scheduling registrations, and it fails fast in simple mode. Prefer EnableScheduling(...) inside AddLumarinNotify(...); UseLumarinNotifyScheduling(...) plus AddLumarinNotifySchedulingEntityFrameworkCore() remains the advanced equivalent.

When the effective policy uses DeferWhenAllRoutesQuietHoursBlocked, SendAsync(...) may return Success = true with AcceptedMode = Deferred, a ScheduledNotificationId, a DeferredUntilUtc timestamp, and a compact DecisionTrace. If any identity still has an immediate route, Lumarin.Notify keeps the immediate path; Release B does not split one request into mixed immediate and scheduled clones.

HTTP endpoints (AspNetCore adapter)

Lumarin.Notify.AspNetCore adds ready-made minimal API endpoints for the notification feed, user preferences, device registration, template admin, and delivery tracking.

builder.Services.AddLumarinNotify(options =>
{
    options.EnableTemplates();
    options.EnableHostedApiDefaults(hosted =>
    {
        hosted.ScopeType = "user";
        // Default identity claim order: ClaimTypes.NameIdentifier, "sub", "oid", "user_id"
        // hosted.IdentityClaimTypes = ["sub", "oid"]; // e.g. raw JWT / MapInboundClaims = false
    });
});

// Optional: register a custom request actor resolver if claim-order configuration is not enough
builder.Services.AddSingleton<ILumarinNotifyRequestActorResolver, YourRequestActorResolver>();

app.MapLumarinNotify();

EnableHostedApiDefaults(...) keeps signed webhook receipt ingestion explicit. Pass configureWebhookReceipts: webhooks => webhooks.SharedSecret = ... when the bundle should map the receipt endpoint, or call EnableWebhookReceipts(...) separately after you configure WebhookReceipts.SharedSecret.

Advanced hosts can still register AddLumarinNotifyAspNetCore(...) directly and use the granular MapLumarinNotify*Api() mappers when they intentionally want lower-level composition.

Signed webhook receipts can also carry additive feedback metadata such as FeedbackKind, provider-native RawStatus / RawCode, ExternalEventId, IdempotencyKey, and a push DeviceId. When those fields are present, they are included in the webhook receipt signature. Duplicate receipts with the same normalized correlation data are ignored, and invalid push-device feedback only deactivates the specific supplied DeviceId.

Default routes (rebaseable via app.MapGroup(...)):

Endpoint Route
Feed GET /api/notify/feed
Unread count GET /api/notify/feed/unread-count
Mark read POST /api/notify/feed/{notificationId}/read
Mark all read POST /api/notify/feed/mark-all-read
Dismiss POST /api/notify/feed/{notificationId}/dismiss
Archive POST /api/notify/feed/{notificationId}/archive
Unarchive DELETE /api/notify/feed/{notificationId}/archive
Dismiss (stable delete alias) DELETE /api/notify/feed/{notificationId}
Bulk mark read POST /api/notify/feed/bulk/mark-read
Bulk archive POST /api/notify/feed/bulk/archive
Bulk unarchive POST /api/notify/feed/bulk/unarchive
Bulk dismiss POST /api/notify/feed/bulk/dismiss
Preferences GET/PUT /api/notify/preferences
Register device POST /api/notify/devices
Delete device DELETE /api/notify/devices/{deviceId}
Templates GET/PUT /api/notify/templates/{templateKey}
Deliveries GET /api/notify/deliveries/{notificationId}
Dead letters GET /api/notify/dead-letters

Feed queries accept optional isRead, category, and lifecycle=Active|Archived|Dismissed|All filters. Feed items now expose optional archivedAt and dismissedAt timestamps. Bulk lifecycle endpoints return NotificationFeedBulkMutationResponse with per-item machine-readable result codes instead of partial-failure transport semantics.

Upgrade note: inbox read, archive, and dismiss state is tracked per recipient. The explicit dismiss route and the stable delete alias both affect only the resolved recipient. A read, archive, or dismiss action for one recipient no longer changes feed or unread state for other recipients who share the same notification.

Security: The ASP.NET Core host adapter registers DevelopmentOnlyLumarinNotifyAuthorizationPolicy by default, which grants all callers access to template admin and tracking endpoints. Register a custom ILumarinNotifyAuthorizationPolicy before deploying to production.

Template sends

var result = await hub.SendFromTemplateAsync(
    scopeType:   "user",
    scopeId:     "user-123",
    templateKey: "order-shipped",
    data:        new { Name = "Alex", OrderId = "456" },
    recipients:  new NotificationRecipients(["user-123"]),
    locale:      "en");

Requires Lumarin.Notify.Templates. Template engine is "scriban" (default) or "liquid".

Channel registration

Each channel is registered separately. Examples:

// SignalR (realtime WebSocket push) via the primary root-builder story
builder.Services.AddLumarinNotify(options =>
{
    options.EnableSignalR(signalR =>
    {
        signalR.HubPath = "/hubs/notifications";
        signalR.RequireAuthenticatedUser = true;
    });
});
app.MapLumarinNotify(); // maps the configured hub with the rest of the enabled surfaces

// Email (read from configuration; fails fast if credentials missing)
builder.Services.AddLumarinNotify(options =>
{
    options.EnableEmail(email =>
    {
        email.FromAddress = builder.Configuration["LumarinNotify:Channels:Email:FromAddress"]!;
        email.FromName = builder.Configuration["LumarinNotify:Channels:Email:FromName"];
    });
    options.UseSendGrid(builder.Configuration["LumarinNotify:Channels:Email:SendGrid:ApiKey"]!);
});

InMemory database (dev / tests)

If you do not need durable persistence at all, prefer the simple profile (AddLumarinNotify(...) with Delivery.EnableBackgroundWorkers = false) instead of this override.

builder.Services.AddDbContext<LumarinNotifyDbContext>(opts =>
    opts.UseInMemoryDatabase("LumarinNotify_Dev"));

builder.Services.AddScoped<INotificationRepository, NotificationRepository>();
builder.Services.AddScoped<ITemplateRepository, TemplateRepository>();
builder.Services.AddScoped<IDeliveryRepository, DeliveryRepository>();
builder.Services.AddScoped<IDeviceRepository, DeviceRepository>();
builder.Services.AddScoped<IDeadLetterRepository, DeadLetterRepository>();
builder.Services.AddScoped<IPreferenceRepository, PreferenceRepository>();
builder.Services.AddScoped<ITemplateAuditRepository, TemplateAuditRepository>();

No migrations needed. All data resets on restart.

Docker Compose

# API-only
docker compose --profile api up --build

# API + frontend
docker compose --profile frontend up --build

Copy .env.example as the starting point for runtime parameters.

Runnable hosts

Lumarin.Notify ships a small set of curated repo entry points that map to the four adoption lanes:

Lane Start with Use when
Minimal passthrough samples/Lumarin.Notify.Sample.EmbeddedHost You want the fastest first run with in-memory defaults and app-owned route prefixes
Embedded durable samples/Lumarin.Notify.Sample.EmbeddedHost with the postgres profile You want the same embedded host shape with PostgreSQL-backed durability
Hosted/reference hosts/Lumarin.Notify.ReferenceHost You want the clearest admin-capable, PostgreSQL-backed reference host and Docker Compose API target
Advanced runtime tools/Lumarin.Notify.Showcase.Console You want deterministic scenario execution for outbox, scheduling preview, and deeper runtime verification

If you are unsure where to start, start with samples/Lumarin.Notify.Sample.EmbeddedHost in its default minimal profile.

See docs/hosts-and-samples.md for the full host-selection guide and project-level caveats.

Shared showcase tooling is separate from those runnable hosts and lives under tools/: tools/Lumarin.Notify.Showcase, tools/Lumarin.Notify.Showcase.AspNetCore, and tools/Lumarin.Notify.Showcase.Console. Treat that slice as repo-owned dev/test infrastructure, not as product packages to publish. The console supports direct local execution with --profile minimal, --profile postgres, --profile postgres-outbox, and --profile postgres-outbox-scheduling; the PostgreSQL-backed profiles require --connection-string or LUMARIN_NOTIFY_POSTGRES_CONNECTION. Use remote execution against a running showcase host such as hosts/Lumarin.Notify.ReferenceHost at /api/showcase or samples/Lumarin.Notify.Sample.EmbeddedHost at /ops/showcase when you need the host-owned HTTP, auth, and routing surface.

Real providers remain opt-in. Start with fake or development-safe channels first, then use the lane-aware provider recipes when you are ready to validate real credentials and delivery paths.

Next Steps

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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 (4)

Showing the top 4 NuGet packages that depend on Lumarin.Notify.EntityFrameworkCore.PostgreSQL:

Package Downloads
Lumarin.Notify.Outbox.EntityFrameworkCore.PostgreSQL

Optional PostgreSQL provider package for Lumarin.Notify outbox EF Core persistence.

Lumarin.Notify.Scheduling.EntityFrameworkCore.PostgreSQL

Preview PostgreSQL provider package for Lumarin.Notify scheduling EF Core persistence.

Lumarin.Notify.Admin.EntityFrameworkCore.PostgreSQL

Optional PostgreSQL provider package for Lumarin.Notify admin EF Core persistence.

Lumarin.Notify.Hosting.Embedded.PostgreSQL

Optional embedded-hosting convenience profile for Lumarin.Notify on PostgreSQL

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.8.0-preview.199 64 5/14/2026
0.8.0-preview.162 66 4/26/2026
0.8.0-preview.161 74 4/26/2026
0.8.0-preview.160 65 4/26/2026
0.8.0-preview.159 55 4/26/2026
0.8.0-preview.158 57 4/26/2026
0.8.0-preview.157 58 4/25/2026
0.8.0-preview.156 59 4/25/2026
0.8.0-preview.155 59 4/25/2026
0.8.0-preview.154 49 4/25/2026
0.8.0-preview.153 54 4/25/2026
0.8.0-preview.150 63 4/25/2026
0.8.0-preview.133 71 4/23/2026
0.8.0-preview.132 58 4/23/2026
0.8.0-preview.130 55 4/23/2026
0.8.0-preview.128 64 4/23/2026
0.8.0-preview.120 139 4/21/2026
0.8.0-preview.115 59 4/18/2026
0.8.0-preview.114 65 4/18/2026
0.8.0-preview.113 65 4/17/2026
Loading failed