Fluens.Web.Discovery 0.7.10

dotnet add package Fluens.Web.Discovery --version 0.7.10
                    
NuGet\Install-Package Fluens.Web.Discovery -Version 0.7.10
                    
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="Fluens.Web.Discovery" Version="0.7.10" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Fluens.Web.Discovery" Version="0.7.10" />
                    
Directory.Packages.props
<PackageReference Include="Fluens.Web.Discovery" />
                    
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 Fluens.Web.Discovery --version 0.7.10
                    
#r "nuget: Fluens.Web.Discovery, 0.7.10"
                    
#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 Fluens.Web.Discovery@0.7.10
                    
#: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=Fluens.Web.Discovery&version=0.7.10
                    
Install as a Cake Addin
#tool nuget:?package=Fluens.Web.Discovery&version=0.7.10
                    
Install as a Cake Tool

Fluens.Web.Discovery

Consul self-registration and a central, blocking-query-refreshed service catalog for Fluens web applications that start on dynamic ports.

The library owns both halves of discovery per ADR 20260529000001:

  • Registration (write) — the application registers itself into Consul after Kestrel binds, using its real post-start address and port, and deregisters on shutdown. A self-healing reconciler re-applies the held registration on an interval (idempotent upsert), surviving Consul auto-deregistration after transient unhealth.
  • Resolution (read) — a single long-polling worker keeps an in-memory IServiceCatalog (the contract lives in Fluens.Web) converged with Consul; the mesh and HTTP clients read the catalog snapshot instead of each issuing their own Consul query. The worker watches both registration topology and health-state transitions, so a peer recovering from critical is picked up within seconds without a topology change.

The library owns the fluens- meta/tag namespace (DiscoveryMeta: fluens-version, fluens-cluster). The hyphen separator is DNS-safe and legal in both Consul meta keys (which reject .) and tags.

Install

dotnet add package Fluens.Web.Discovery

Usage

builder.AddFluens()
    .AddWeb()
    .AddDiscovery(); // reads "Fluens:Web:Discovery"; no-op when Enabled == false

Both AddDiscovery() and the idempotent TryAddDiscovery() accept an optional Action<DiscoveryOptions>? callback that is honoured and validated even with no matching Fluens:Web:Discovery configuration section present.

Configuration (appsettings.json):

{
  "Fluens": {
    "Web": {
      "Discovery": {
        "Enabled": true,
        "Url": "http://localhost:8500",
        "AdvertisedHost": null,
        "ClusterId": "cluster-1",
        "Tags": [ "grpc" ],
        "Meta": { "region": "eu-west" },
        "ReregisterIntervalSeconds": 30,
        "HealthCheckPath": "/health",
        "HealthCheckIntervalSeconds": 10,
        "DeregisterCriticalServiceAfterSeconds": 60,
        "CatalogWaitSeconds": 300
      }
    }
  }
}

Options

Option Default Meaning
Enabled true Master flag; false makes AddDiscovery() a no-op.
Url Consul agent base URL. Required when enabled.
AdvertisedHost null Overrides the bound host (wildcard/Docker/NAT/cross-host). When unset the bound address is trusted.
ClusterId null When set, published as the cluster service meta.
Tags [] Extra tags merged with auto tags.
Meta {} Extra meta merged with auto version/cluster (user wins on collision).
ReregisterIntervalSeconds 30 Interval of the idempotent-upsert reconciler.
HealthCheckPath /health HTTP check path used when a health source is configured.
HealthCheckIntervalSeconds 10 Consul check poll interval.
DeregisterCriticalServiceAfterSeconds 60 Consul DeregisterCriticalServiceAfter auto-cleanup.
CatalogWaitSeconds 300 Max wait time (seconds) of each catalog blocking query (>0).

Address normalization

The advertised address is computed once from IServerAddressesFeature and held. Wildcard/loopback bind tokens ([::], [::1], *, 0.0.0.0) are normalized to localhost. Set AdvertisedHost to advertise a routable host instead of the bound one.

Health check

A health check is registered only when a source is configured, with precedence:

  1. an explicit health path passed to the registration call,
  2. else IHealthEndpointInfo resolved from DI (e.g. provided by Fluens.Web.Healthchecks),
  3. else none.

When present it is a Consul HTTP check with DeregisterCriticalServiceAfter as the native auto-cleanup.

Resolution

AddDiscovery() registers a singleton IServiceCatalog (ServiceCatalog) and a CatalogRefreshWorker. The worker runs two concurrent Consul blocking queriesCatalog.Services (registration topology) and Health.State(HealthStatus.Any) (health transitions), each on its own WaitIndex and bounded by CatalogWaitSeconds — and rebuilds the snapshot as soon as either advances. It then reads the current topology plus per-service Health.Service(passingOnly), assembles an immutable snapshot, and swaps it into the catalog. Reacting to the health index means a peer's critical→passing transition wakes a refresh within seconds even when no service was added or removed. The query that loses the race is cancelled and awaited before the pass returns, so it releases its connection immediately instead of lingering abandoned until the next pass re-arms it — the cancelled query simply re-issues with its unchanged WaitIndex next time, so no change is lost. Consumers read the catalog directly:

// Resolve a single healthy endpoint (round-robins across every instance of the name, ignoring tags/meta).
ResolvedServiceEndpoint? endpoint = catalog.Resolve("orders.eu");

// Enumerate services by tag (and optional meta), filtered at instance granularity: an instance
// contributes only when it, alone, carries the tag and matches every metaMatch pair, so two instances
// of one service with different "fluens-cluster" meta each resolve under their own filter.
IReadOnlyList<DiscoveredService> matches = catalog.List(
    "grpc",
    new Dictionary<string, string> { ["fluens-cluster"] = "cluster-1" });

// React to topology changes.
IChangeToken token = catalog.GetChangeToken();

Sibling libraries that require the catalog (for example the messaging mesh) call the idempotent TryAddDiscovery() — it wires discovery exactly once, so the catalog worker is never registered twice.

ADR 20260529000004 — endpoint resolution only. The catalog answers where to deliver now; it is not the source of mesh peer subscriptions, and List("fluens-mesh", …) is not how the mesh finds its peers. Peer identity comes from a durable Consul-KV subscription store (fluens/<ClusterId>/subscriptions/<app>, read over the same IConsulClient this library registers); the mesh resolves each owed peer's live address with a plain catalog.Resolve(app), so a peer going health-critical drops from the catalog without dropping from the mesh's owed-app set. Because that KV store is not re-derivable from a fresh Consul, run Consul persistently (-server -bootstrap-expect=1 -data-dir + a named volume), never -dev, wherever mesh durability is load-bearing.

Contributor model

A sibling library can advertise additive tags/meta into this application's registration without Discovery depending on it, by registering an IServiceRegistrationContributor:

internal sealed class MeshContributor : IServiceRegistrationContributor
{
    public ServiceRegistrationContribution Contribute() =>
        new(["fluens-mesh"], new Dictionary<string, string>(StringComparer.Ordinal));
}

services.AddSingleton<IServiceRegistrationContributor, MeshContributor>();

Contributions merge into the descriptor with auto → contributor → user precedence; tags are unioned with ordinal de-duplication. The mesh uses this to advertise the fluens-mesh tag on its own registration; the tag identifies mesh-capable services in the catalog, but peer discovery itself goes through the Consul-KV subscription store + Resolve, not a List("fluens-mesh", …) call — see the ADR note above.

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 (2)

Showing the top 2 NuGet packages that depend on Fluens.Web.Discovery:

Package Downloads
Fluens.Web.Messaging.Dashboard

Cluster-wide monitoring surface for the Fluens cross-application messaging mesh: dead-letter queries, inbox lag, and stats snapshots.

Fluens.Web.Messaging

P2P HTTP/Protobuf mesh adapter for cross-application Fluens messaging, with shared-key signed cluster authentication.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.7.10 0 9/13/2026
0.7.9 52 9/11/2026
0.7.8 49 9/10/2026
0.7.7 55 9/10/2026
0.7.6 150 7/1/2026
0.7.5 148 6/22/2026
0.7.4 158 6/18/2026
0.7.2 144 6/18/2026
0.7.1 151 6/18/2026