Skip to main content

Configuration

All SDK configuration is loaded from the standard .NET configuration sources (appsettings.json, environment variables, secrets). AddBeaconTowerProvider<TAdapter> binds the sections below at startup.

Provider identity

{
"Provider": {
"Id": "my-provider"
}
}

Outbound integrations (OAuth2 client-credentials)

Aggregator-backed providers register named outbound integrations with services.AddOutboundOAuth2Client<TClient>(configuration, name). Each named integration has its own configuration block:

{
"Outbound": {
"<name>": {
"TokenUrl": "https://...",
"ClientId": "<from secret store>",
"ClientSecret": "<from secret store>",
"Scope": null,
"Audience": null,
"RefreshSkewSeconds": 60
}
}
}
OptionDefaultDescription
TokenUrl(required)OAuth2 token endpoint URL.
ClientId(required)Client identifier. Source from secret store.
ClientSecret(required)Client secret. Source from secret store.
ScopenullOptional space-separated scope list.
AudiencenullOptional audience parameter (some providers require it).
RefreshSkewSeconds60Refresh tokens this many seconds before expiry.

See Outbound Integrations for the full pattern.

OptionDefaultDescription
Idconfigured by host (e.g. "mqtt")Unique provider identifier. Used in NATS subjects, telemetry headers, CloudEvent source.

ProviderSdkOptions is set from the Action<ProviderSdkOptions> you pass to AddBeaconTowerProvider. The reference host reads Provider:Id from configuration and forwards it.

Database

{
"ConnectionStrings": {
"beacontower-mqttproviderdb": "Host=localhost;Database=mqtt;Username=postgres;Password=..."
}
}

The SDK uses BeaconTower.Data.PostgreSql with Dapper. DbUp migrations run at startup unless ASPNETCORE_ENVIRONMENT is Test or Testing.

NATS

{
"Nats": {
"Url": "nats://localhost:4222",
"User": "",
"Password": "",
"TlsCertFile": "",
"TlsKeyFile": "",
"MonitoringPort": 8222,
"MonitoringTimeoutSeconds": 5,
"ClusterReplicaThreshold": 3,
"Events": {
"ServiceName": "my-provider"
}
}
}
OptionDefaultDescription
Url"nats://localhost:4222"NATS server URL.
User / PasswordemptyOptional NATS authentication.
TlsCertFile / TlsKeyFileemptyOptional client TLS material.
MonitoringPort8222NATS monitoring HTTP port — queried at startup to detect cluster topology.
MonitoringTimeoutSeconds5HTTP timeout for the monitoring probe.
ClusterReplicaThreshold3Minimum cluster size to use NumReplicas=3 for the JetStream streams the SDK provisions.
Events:ServiceNamederivedService name embedded in the CloudEvents source (/providers/{ServiceName} by convention).

Nats:Events is consumed by BeaconTower.Events.AddNatsCloudEventPublisher; additional fields (e.g. JetStream stream name, retention) are documented there.

Telemetry pipeline

{
"TelemetryPipeline": {
"ChannelCapacity": 50000,
"BatchSize": 256,
"FlushIntervalMs": 50,
"MaxReportedConcurrency": 16
}
}

See Telemetry Pipeline › Configuration.

Bus API

The BusApi section is split across two concerns: the keyed connection (consumed by AddBeaconTowerBusApi from BeaconTower.BusApi) and the handler tunables (consumed by the SDK's BusApiConsumers background service). They share a configuration root for ergonomic reasons.

Keyed connection (AddBeaconTowerBusApi)

{
"BusApi": {
"NatsUrl": "nats://bus.internal:4222",
"User": "<bus-user>",
"Password": "<from secret store>",
"TlsCertFile": "",
"TlsKeyFile": ""
}
}
OptionDefaultDescription
NatsUrlrequiredCanonical-wire NATS URL. Drives the keyed INatsConnection ("btbus") used by the provider control-plane RPC and NatsTemplateClient.
User / PasswordemptyOptional bus authentication — source from a secret store, never from appsettings.json checked into source control.
TlsCertFile / TlsKeyFileemptyOptional client TLS material. Required in production deployments where the bus enforces mTLS.

Wire AddBeaconTowerBusApi before AddBeaconTowerProvider in Program.cs — the SDK resolves the keyed INatsConnection as a dependency during provider registration:

builder.Services.AddBeaconTowerBusApi(opts =>
{
opts.NatsUrl = builder.Configuration["BusApi:NatsUrl"];
opts.User = builder.Configuration["BusApi:User"];
opts.Password = builder.Configuration["BusApi:Password"];
});

builder.Services.AddBeaconTowerProvider<MyAdapter>(builder, options =>
{
options.ProviderId = builder.Configuration["Provider:Id"];
});

Nats:Url (data plane — telemetry + JetStream events) and BusApi:NatsUrl (canonical wire — control plane) are independently configurable. In single-node development they typically point at the same NATS instance; production deployments commonly separate them.

Handler tunables (BusApiConsumers)

{
"BusApi": {
"MaxConcurrentHandlers": 50,
"MaxMethodTimeoutSeconds": 300,
"SubscriptionShutdownTimeoutSeconds": 5,
"HandlerDrainTimeoutSeconds": 10
}
}
OptionDefaultDescription
MaxConcurrentHandlers50Semaphore bound on concurrent NATS request handlers (per category).
MaxMethodTimeoutSeconds300Server-side cap on cloud-to-device method-invocation timeout.
SubscriptionShutdownTimeoutSeconds5Timeout for subscription loops to exit during shutdown.
HandlerDrainTimeoutSeconds10Timeout for in-flight handlers to complete during shutdown.

Credentials

{
"Credentials": {
"MasterKey": "<base64-encoded-32-byte-key>",
"PreviousMasterKeys": [],
"BcryptWorkFactor": 10
}
}
OptionDefaultDescription
MasterKeyrequiredBase64-encoded 256-bit AES key. Encrypts device credentials at rest with AES-256-GCM.
PreviousMasterKeys[]Older keys, oldest → newest. Decrypt-side fallback for rotation; index 0 = version 1, etc.
BcryptWorkFactor10Bcrypt cost factor for broker-side password hashing.

See Credentials for the rotation runbook.

Webhook (CloudEvents transport)

{
"Webhook": {
"TimeoutSeconds": 30,
"Targets": [
{
"Id": "primary",
"Url": "https://example.com/webhooks/beacontower",
"Secret": "<hmac-shared-secret>"
}
]
}
}
OptionDefaultDescription
TimeoutSeconds30Per-request HTTP timeout for the outbound webhook publisher.
Targets[].IdrequiredRouting-target identifier; surfaces on per-target metrics tags.
Targets[].UrlrequiredEndpoint that receives the CloudEvents JSON envelope.
Targets[].SecretrequiredHMAC-SHA256 shared secret used for X-Webhook-Signature over the raw body. An empty secret is a configuration error.

An empty Targets list disables the webhook transport — NATS JetStream still publishes.

Diagnostics

{
"Diagnostics": {
"Enabled": true,
"ChannelCapacity": 100000,
"StatusFlushIntervalSeconds": 5,
"RetentionDays": 30,
"LogBatchSize": 100,
"LogBatchIntervalMs": 1000,
"StatusDrainBatchSize": 256,
"StatusUpsertConcurrency": 16,
"FlapThreshold": 5,
"FlapWindowSeconds": 300,
"FlapCooldownSeconds": 600,
"SummaryMaxStalenessSeconds": 30,
"FilterPollIntervalSeconds": 30,
"PurgeIntervalHours": 24
}
}
OptionDefaultDescription
EnabledtrueMaster toggle. When false, DiagnosticsEnabledMiddleware short-circuits all /api/diagnostics/* endpoints with HTTP 404.
ChannelCapacity100,000Burst-absorption buffer for status/log events. Drops counted via status.drops.total / connection_log.drops.total.
StatusFlushIntervalSeconds5Flush cadence for batched LastSeenAt updates.
RetentionDays30Connection-log retention before purge.
LogBatchSize / LogBatchIntervalMs100 / 1000Connection-log batcher.
StatusDrainBatchSize256Per-cycle drain size for DeviceStatusProjection.
StatusUpsertConcurrency16Concurrent device-status upserts.
FlapThreshold / FlapWindowSeconds / FlapCooldownSeconds5 / 300 / 600Flap detector — 5 disconnects in 5 min flips flapping, with 10 min cooldown.
SummaryMaxStalenessSeconds30Cache TTL for the /devices/summary aggregate (spec ceiling).
FilterPollIntervalSeconds30Active-log-filter poll interval.
PurgeIntervalHours24Log retention sweep cadence.

Filters are an allow-list — no logs are stored without an active filter.

Provisioning

{
"Provisioning": {
"RetentionDays": 7,
"HardPruneIntervalHours": 24,
"HardPruneHourUtc": 2,
"HardPruneEnabled": true,
"TemplateCacheTtlSeconds": 60,
"TemplateResolveTimeoutSeconds": 5
}
}
OptionDefaultDescription
RetentionDays7Days to retain soft-deleted device rows before hard-deletion.
HardPruneIntervalHours24Cadence between hard-prune sweeps after the first.
HardPruneHourUtc2UTC hour-of-day of the first daily sweep (0–23).
HardPruneEnabledtrueWhen false, HardPruneBackgroundService is not registered.
TemplateCacheTtlSeconds60Positive and negative cache TTL for NatsTemplateClient. Lower it only if you accept the extra core-management traffic — the floor matches operator authoring expectations.
TemplateResolveTimeoutSeconds5Per-call application timeout for template resolve. Combined with RequestNoResponders=true for fast-fail when no core-management consumer is registered. Must be ≤ the bus-api consumer's overall budget.

The two template options govern NatsTemplateClient — the SDK's default ITemplateClient. See Template Resolution for the wire shape, error-code map, and replacement guidance.

Transformer circuit breaker

The per-device transformer circuit breaker is wired by AddProviderSdk() (called inside AddBeaconTowerProvider). Bind under Transformer to override:

{
"Transformer": {
"FailureThreshold": 10,
"FailureWindow": "00:01:00",
"OpenCircuitTimeout": "00:00:30"
}
}
OptionDefaultDescription
FailureThreshold10Failures within FailureWindow that open the per-device circuit.
FailureWindow60 sSliding window over which failures are counted.
OpenCircuitTimeout30 sCool-down before retrying a device with an open circuit.

Authentication (host-level)

JWT Bearer is wired by the host, not the SDK, and follows the canonical pattern in BeaconTower.Core.API (config section AuthenticationOptions:*, OIDC discovery via MetadataAddress, ValidateIssuer = true as the only explicit validator). The reference host validates required production configuration and fails fast on missing values:

ConfigurationRequired in Production
AuthenticationOptions:MetadataAddressyes (OIDC discovery document URL)
AuthenticationOptions:Authorityyes (no placeholder values)
AuthenticationOptions:Audienceyes
Credentials:MasterKeyyes
ConnectionStrings:beacontower-mqttproviderdbyes
BusApi:NatsUrlyes (canonical-wire control plane — provider RPC + template resolution)

Other token-validation parameters (ValidateAudience, ValidateLifetime, ValidateIssuerSigningKey) are left at Microsoft's defaults — all true — so only ValidateIssuer = true is set explicitly.

In Development the reference host swaps in DevBypassAuthHandler, which auto-authenticates with a DeviceAdmin role claim.

Environment variable mapping

All settings can be overridden via environment variables using __ as the separator:

Provider__Id=my-provider
ConnectionStrings__beacontower-mqttproviderdb="Host=..."
Nats__Url=nats://nats:4222
Nats__Events__ServiceName=my-provider
TelemetryPipeline__BatchSize=512
TelemetryPipeline__ChannelCapacity=100000
Credentials__MasterKey=<base64>
Webhook__Targets__0__Id=primary
Webhook__Targets__0__Url=https://example.com/webhooks
Webhook__Targets__0__Secret=<hmac-secret>
Diagnostics__Enabled=true
BusApi__NatsUrl=nats://bus.internal:4222
BusApi__User=<bus-user>
BusApi__Password=<from-secret-store>
BusApi__MaxConcurrentHandlers=64
Provisioning__TemplateCacheTtlSeconds=60
Provisioning__TemplateResolveTimeoutSeconds=5