SeliseBlocks.Genesis.OS
4.6.3
See the version list below for details.
dotnet add package SeliseBlocks.Genesis.OS --version 4.6.3
NuGet\Install-Package SeliseBlocks.Genesis.OS -Version 4.6.3
<PackageReference Include="SeliseBlocks.Genesis.OS" Version="4.6.3" />
<PackageVersion Include="SeliseBlocks.Genesis.OS" Version="4.6.3" />
<PackageReference Include="SeliseBlocks.Genesis.OS" />
paket add SeliseBlocks.Genesis.OS --version 4.6.3
#r "nuget: SeliseBlocks.Genesis.OS, 4.6.3"
#:package SeliseBlocks.Genesis.OS@4.6.3
#addin nuget:?package=SeliseBlocks.Genesis.OS&version=4.6.3
#tool nuget:?package=SeliseBlocks.Genesis.OS&version=4.6.3
SeliseBlocks.Genesis
The .NET service foundation for SELISE <blocks /> microservices. One package wires up configuration and secrets, JWT authentication, multi-tenant request handling, MongoDB, Redis, Azure Service Bus and RabbitMQ messaging, gRPC, and observability (logs, metrics, traces).
Requires .NET 10 (net10.0). Full repository documentation: https://github.com/SELISEdigitalplatforms/blocks-genesis-net.
Installation
dotnet add package SeliseBlocks.Genesis
Quick start: API service
using Blocks.Genesis;
const string serviceName = "MyBlocksApi";
// Configure secrets and logging first. Pass VaultType.Azure or VaultType.OnPrem,
// or let ResolveVaultType() read the BLOCKS_VAULT_TYPE environment variable.
await ApplicationConfigurations.ConfigureLogAndSecretsAsync(
serviceName, ApplicationConfigurations.ResolveVaultType());
var builder = WebApplication.CreateBuilder(args);
ApplicationConfigurations.ConfigureApiEnv(builder, args);
ApplicationConfigurations.ConfigureKestrel(builder);
var services = builder.Services;
ApplicationConfigurations.ConfigureServices(services, new MessageConfiguration
{
ServiceName = serviceName,
AzureServiceBusConfiguration = new AzureServiceBusConfiguration
{
Queues = ["demo_queue"],
Topics = ["demo_topic"],
},
});
ApplicationConfigurations.ConfigureApi(services, serviceName);
var app = builder.Build();
ApplicationConfigurations.ConfigureMiddleware(app);
await app.RunAsync();
Quick start: worker service
Register one IConsumer<T> per message type; messages are routed to consumers by payload type name.
using Blocks.Genesis;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
const string serviceName = "MyBlocksWorker";
await ApplicationConfigurations.ConfigureLogAndSecretsAsync(
serviceName, ApplicationConfigurations.ResolveVaultType());
var messageConfiguration = new MessageConfiguration
{
ServiceName = serviceName,
AzureServiceBusConfiguration = new AzureServiceBusConfiguration
{
Queues = ["demo_queue"],
Topics = ["demo_topic"],
},
};
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddSingleton<IConsumer<DemoMessage>, DemoMessageConsumer>();
ApplicationConfigurations.ConfigureWorker(services, messageConfiguration);
})
.Build();
await host.RunAsync();
public sealed record DemoMessage(string Text);
public sealed class DemoMessageConsumer : IConsumer<DemoMessage>
{
public Task Consume(DemoMessage context)
{
Console.WriteLine($"Received: {context.Text}");
return Task.CompletedTask;
}
}
Notifications
services.AddBlocksNotifications(); // after the genesis services (API or worker)
publisher.Publish(new NotificationRequest
{
Name = "iam-user-updated", // KV notification-config:<Name> in the tenant DB, else the root built-in
NamedUserIds = [affectedUserId],
OrganizationId = recordOrgId, // used only when there is no user token
Payload = new { Fields = changed }, // JSON, max 256 KB
});
Publishreturns at once and never throws. A background worker reads a 1000-item buffer (full: dropped and logged).- The tenant configuration decides receivers (level, scope, filters), channels (inbox, live, push) and the rate limit.
- Live goes to
NotificationHubgroups over the Redis backplane; the hub itself is mapped by blocks-logic only. - Push sends
PushRequestedto queueblocks_notification_push_listener(consumed by the logic worker).
Actions
// After a successful save. IActionPublisher is registered by ConfigureServices (API and worker).
await actionPublisher.PublishAsync("iam-user-created", new { userId, organizationId });
- Reads the tenant's entry of that one action, key
actions:<name>inKeyValueStores(no cache, 2 s limit). Nothing is sent when there is no entry or all its targets are off. The rootactionslist is the catalog; Genesis does not read it. - Keeps only the top-level data fields named in the action's
Payload(case-sensitive, System.Text.Json default names); no data gives{}. - Sends one
BlocksActionper target that is on:blocks_logic_action_workflow_listener,blocks_logic_action_function_listener,blocks_os_action_webhook_listener. Tenant, user and trace travel in the message properties. organizationIdnull takes the organization fromBlocksContext; empty means none.- Best-effort: never throws; an envelope over 64 KB is dropped and logged; the data is never logged.
Configuration
ConfigureLogAndSecretsAsync loads a .env file when one exists at or above the working directory, then resolves secrets from Azure Key Vault (VaultType.Azure) or environment variables (VaultType.OnPrem, prefixed with BlocksSecret__). DatabaseConnectionString and CacheConnectionString are required; startup fails with a descriptive error when they are missing.
Log and trace forwarding (LMT)
By default, logs are written to the console and to MongoDB (BlocksSecret__LogConnectionString), and traces to MongoDB (BlocksSecret__TraceConnectionString). To forward logs and traces to the central LMT pipeline over a message bus instead, set:
# Environment variable (or .env entry)
ServiceBusConnectionString=<your-service-bus-connection-string>
# Optional retry tuning
MaxRetries=3
MaxFailedBatches=100
MaxRetries and MaxFailedBatches can also be set in appsettings.json:
{
"Lmt": {
"MaxRetries": 3,
"MaxFailedBatches": 100
}
}
| Setting | Required | Source | Default |
|---|---|---|---|
ServiceBusConnectionString |
Optional | Environment variable | unset (logs and traces go to console and MongoDB) |
MaxRetries |
Optional | appsettings.json or environment |
3 |
MaxFailedBatches |
Optional | appsettings.json or environment |
100 |
Important: keep real connection strings out of source control. Add .env to your .gitignore:
.env
.env.local
.env.*.local
Middleware pipeline (API)
HSTS -> CORS -> Health endpoints (/ping, /health/live, /health/ready) -> Swagger (when configured) -> Routing -> TenantValidation -> GlobalExceptionHandler -> Authentication -> Authorization -> UserRateLimit -> OnboardingApiAccess -> Antiforgery -> Controllers
Root cross-tenant API access
OnboardingApiAccess applies only when a root-tenant token calls another tenant (its tenant_id is the root tenant and the request's tenant, from the x-blocks-key header or query, is a different one). Such a request may reach only the paths listed in the AllowedApis array of the root database's IdentityConfigurations document. A path matches when it equals an entry case-insensitively, ignoring leading and trailing /. Every other request passes through without reading that list.
- The list is read at most once per 5 minutes per service instance, including when it is missing or empty, so a change to
AllowedApisapplies within 5 minutes. Non-string and blank entries are ignored. - A path not in the list gets
403with"Forbidden: Cross_Tenant_Access_Not_Allowed". - If the read fails or takes more than 2 s, the last good list keeps being used and the read is retried after 30 s. If no list has loaded yet, the request gets
503 Service UnavailablewithRetry-After: 30and"Service_Unavailable: Os_Allowed_Apis_Unavailable"until a read succeeds; retries are again 30 s apart.
Rate limiting
Authorized and protected endpoints are rate limited per service and per authenticated subject (user_id, or client_id for client_credentials tokens), with counts shared through Redis under ratelimit:{ServiceName}:{TenantId}:{user|client}:{id}. Public endpoints are never limited. Services can now return 429 Too Many Requests (gRPC: RESOURCE_EXHAUSTED).
- Limit (requests per second), resolved once at startup:
UserRateLimitPerSecondenv var, then theUserRateLimitPerSecondvault secret (BlocksSecret__UserRateLimitPerSecondon-prem), then100. - Going over the limit blocks the subject for 30 s:
429,Retry-After: 30, Problem Details body"title":"Rate Limit Exceeded". Clients should wait forRetry-Afterbefore retrying. - Allowed responses carry
RateLimit-Policy: "user";q=<limit>;w=1andRateLimit: "user";r=<remaining>;t=<reset>. - If Redis is slow (over 50 ms) or down, requests are allowed.
HttpServiceno longer opens its circuit breaker on a downstream 429; it retries a 429 only whenRetry-Afterfits in the request timeout.
See the repository README for the full contract.
Local development
Start local infrastructure (MongoDB, Redis, RabbitMQ) from the repository root:
docker-compose up -d
Use .env.example as the baseline for environment variables.
Migration notes
ConsumerMessage.ScheduledEnqueueTimeUtcis the corrected property name;SccheduledEnqueueTimeUtcremains as an obsolete alias.ConfigureAzureServiceBusreplacesConfigerAzureServiceBus; the old name remains as an obsolete shim and will be removed in the next major version.SecretEndPointAttributereplaces the misspelledSecretEnpPointAttribute, which has been removed.WorkflowEntity.Settingschanges fromDictionary<string, string>toWorkflowSettings?, which holds the per-workflow failureAlert. This is a deliberate breaking change with no[Obsolete]alias; blocks-logic adopts it in blocks-logic#380. No data migration is needed: a storedSettingsthat is missing,null,{}or holds unknown keys still loads, and a default workflow now writesSettings: { Alert: null }instead ofSettings: {}.- The one incompatible stored shape is a
Settings.Alertthat is neither a document nor null, e.g.{ "Alert": "text" }; reading it throwsFormatException. - Rules for adding workflow settings: every new property is nullable and null means "use the default"; only add properties, never rename or retype one; every class has
[BsonIgnoreExtraElements]; enums are stored as numbers. OnboardingApiAccessMiddlewarenow reads the rootAllowedApisonly for root cross-tenant requests, caches it for 5 minutes (an empty or missing list included), and answers those requests with503andRetry-After: 30instead of a500when the list cannot be loaded and none has loaded before. A list that was already loaded is now refreshed every 5 minutes instead of only on restart. Its public constructor now throwsArgumentNullExceptionfor a nullblocksSecret, and it creates its root MongoDB client when the pipeline is built, so an invalidDatabaseConnectionStringfails at startup instead of at the first request.- 4.6.2 removes the Blocks Event Trigger forwarder (breaking):
IBlocksEventTriggerForwarder,BlocksEventTriggerMessage,BlocksEventCatalogEntry,BlocksEventTriggerConstantsand the message clients' forwarder constructors are gone. Message clients no longer forward sends; publish an action withIActionPublisherinstead. - 4.6.3: the publisher reads the tenant entry
actions:<name>(ActionConstants.ActionKey, one entry per switched-on action, written by OS) instead of the tenantactionslist. A tenantactionslist written by 4.6.2-era OS is ignored.
License
MIT. See the repository LICENSE file.
| Product | Versions 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. |
-
net10.0
- AspNetCore.HealthChecks.MongoDb (>= 9.0.0)
- AspNetCore.HealthChecks.Redis (>= 9.0.0)
- Azure.Identity (>= 1.21.0)
- Azure.Messaging.ServiceBus (>= 7.20.2)
- Azure.Security.KeyVault.Certificates (>= 4.9.0)
- Azure.Security.KeyVault.Secrets (>= 4.11.0)
- DotNetEnv (>= 3.2.0)
- Google.Protobuf (>= 3.35.1)
- Grpc.AspNetCore (>= 2.80.0)
- Grpc.Net.Client (>= 2.80.0)
- Grpc.Tools (>= 2.82.0)
- MessagePack (>= 2.5.303)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 10.0.7)
- Microsoft.AspNetCore.SignalR.StackExchangeRedis (>= 10.0.7)
- Microsoft.OpenApi (>= 2.7.5)
- MongoDB.Driver (>= 3.8.0)
- OpenTelemetry (>= 1.15.3)
- OpenTelemetry.Extensions.Hosting (>= 1.15.3)
- OpenTelemetry.Instrumentation.AspNetCore (>= 1.15.2)
- OpenTelemetry.Instrumentation.Http (>= 1.15.1)
- OpenTelemetry.Instrumentation.Process (>= 1.12.0-beta.1)
- OpenTelemetry.Instrumentation.Runtime (>= 1.15.1)
- Polly (>= 8.6.6)
- RabbitMQ.Client (>= 7.2.1)
- Rollbar (>= 5.2.2)
- SeliseBlocks.LMT.Client (>= 10.0.1)
- Serilog (>= 4.3.1)
- Serilog.AspNetCore (>= 10.0.0)
- Serilog.Enrichers.Environment (>= 3.0.1)
- Serilog.Enrichers.HttpContext (>= 8.3.0)
- Serilog.Enrichers.OpenTelemetry (>= 1.0.1)
- Serilog.Sinks.Console (>= 6.1.1)
- Serilog.Sinks.MongoDB (>= 7.2.0)
- SharpCompress (>= 0.48.0)
- Snappier (>= 1.3.1)
- StackExchange.Redis (>= 2.12.14)
- Swashbuckle.AspNetCore (>= 10.1.7)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on SeliseBlocks.Genesis.OS:
| Package | Downloads |
|---|---|
|
SeliseBlocks.Secrets.OS
Secret management for Blocks: metadata and audit in MongoDB on the tenant's own connection, values in Azure Key Vault. |
|
|
SeliseBlocks.ReleaseDriver.OS
Blocks Release Driver |
|
|
SeliseBlocks.MonitorDriver.OS
Blocks Monitor Driver - Monitor and Health APIs |
|
|
SeliseBlocks.Localization.OS
Blocks Eurolm Driver |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.6.4 | 277 | 11/10/2026 |
| 4.6.3 | 899 | 11/5/2026 |
| 4.6.2 | 288 | 11/4/2026 |
| 4.6.1 | 101 | 11/4/2026 |
| 4.6.0 | 209 | 11/3/2026 |
| 4.5.9 | 156 | 11/1/2026 |
| 4.5.8 | 266 | 10/29/2026 |
| 4.5.7 | 1,619 | 10/29/2026 |
| 4.5.6 | 85 | 10/29/2026 |
| 4.5.5 | 194 | 10/29/2026 |
| 4.5.4 | 910 | 10/29/2026 |
| 4.5.3 | 83 | 10/28/2026 |
| 4.5.2 | 193 | 10/28/2026 |
| 4.5.1 | 208 | 10/28/2026 |
| 4.5.0 | 80 | 10/28/2026 |
| 4.4.9 | 143 | 10/27/2026 |
| 4.4.8 | 261 | 10/26/2026 |
| 4.4.8-preview.1 | 102 | 10/26/2026 |
| 4.4.7 | 124 | 10/26/2026 |
| 4.4.7-preview.2 | 102 | 10/26/2026 |