Configuration
Featly is configured through the standard ASP.NET Core configuration pipeline
(appsettings.json, environment variables, user secrets) plus, for
runtime-editable settings, the database.
Three-layer precedence
Section titled “Three-layer precedence”Settings resolve in this order, highest wins (ADR-0016):
- Hardcoded defaults — sensible defaults baked into the code.
appsettings.json/ environment variables — the bootstrap baseline.- Database — overrides both, for settings an operator edits in the dashboard.
Some settings are bootstrap-only and cannot live in the database, because
they are read before the database connection exists: the connection string, the
AutoMigrate flag, Kestrel URLs, and the bootstrap admin identifier.
Featly:Server
Section titled “Featly:Server”Bound from configuration into FeatlyServerOptions.
| Key | Default | Notes |
|---|---|---|
AdminApiKey |
"" |
Static bearer key for the admin API + dashboard login. A bootstrap shortcut — prefer minted, user-bound keys. Treat as a secret. |
SdkApiKey |
"" |
Static bearer key for the SDK API (/api/sdk/*). Treat as a secret. |
AutoCreateDefaultProject |
true |
Create a default project + environment on first boot. |
DefaultProjectKey |
default |
Key of the auto-created project. |
DefaultEnvironmentKey |
development |
Key of the auto-created environment. |
Featly:Server:Features
Section titled “Featly:Server:Features”Opt-out toggles for entire feature areas (ADR-0024).
All default true. Disabling an area drops its HTTP endpoints and removes it from
the dashboard nav. See Modularity.
| Key | Default | Notes |
|---|---|---|
Flags |
true |
Feature flags. |
Configs |
true |
Dynamic configuration. |
Segments |
true |
Reusable audiences. |
Experiments |
true |
A/B testing + exposure events. |
Approvals |
true |
Approval workflows / pending changes. |
Webhooks |
true |
Outbound webhook delivery. |
Audit |
true |
Audit log. |
Rbac |
true |
Role management UI/API (the permission checks always run). |
Featly:Storage:Sqlite
Section titled “Featly:Storage:Sqlite”Bound into SqliteFeatlyStoreOptions.
| Key | Default | Notes |
|---|---|---|
ConnectionString |
Data Source=featly.db |
Any Microsoft.Data.Sqlite connection string. Bootstrap-only. |
AutoMigrate |
true |
Apply pending EF Core migrations at startup. Set false in production and run featly db migrate from your release pipeline. Bootstrap-only. |
Featly:Authorization
Section titled “Featly:Authorization”Bound into FeatlyAuthorizationOptions. DB-overridable (AutoProvisionMode).
| Key | Default | Notes |
|---|---|---|
BootstrapAdminIdentifier |
"" |
When set, the identifier (email / OIDC sub) is treated as admin on first boot and the user row is seeded. Bootstrap-only. An alternative to featly bootstrap-admin. |
AutoProvisionMode |
Open |
Open: an authenticated user with no role assignment gets the viewer floor. Closed: deny unless an explicit assignment grants access. |
Featly:Audit
Section titled “Featly:Audit”Bound into FeatlyAuditOptions. DB-overridable. Controls audit-log retention.
| Key | Default | Notes |
|---|---|---|
RetentionDays |
0 |
0 keeps audit entries forever. A positive value prunes entries older than N days (a background worker prunes every 6 hours). |
Featly:ApprovalDefaults
Section titled “Featly:ApprovalDefaults”Bound into FeatlyApprovalDefaultsSettings. DB-overridable. The default
approval-policy templates applied to an environment that has no explicit policy —
one template for production-like environments (Prod) and one for the rest
(NonProd). An environment whose key contains prod uses the Prod template.
Each template (Prod, NonProd) has:
| Key | Default | Notes |
|---|---|---|
Required |
false |
Whether mutations require approval. |
MinApprovals |
1 |
Number of approvals needed before a change applies. |
AuthorCanApproveOwnChange |
false |
Whether the proposer may approve their own change. |
AllowEmergencyBypass |
true |
Whether an audited break-glass bypass is permitted. |
Featly:Webhooks
Section titled “Featly:Webhooks”Bound into WebhookOptions — the delivery worker’s tuning. DB-overridable.
| Key | Default | Notes |
|---|---|---|
PollInterval |
00:00:05 |
How often the worker drains the delivery queue. |
BatchSize |
50 |
Max deliveries claimed per poll. |
MaxAttempts |
6 |
Attempts before a delivery is dead-lettered. |
BaseRetryDelay |
00:00:10 |
Exponential backoff base (base · 2^(n-1)). |
MaxRetryDelay |
00:30:00 |
Backoff cap. |
RequestTimeout |
00:00:10 |
Per-delivery HTTP timeout. |
Featly:Telemetry
Section titled “Featly:Telemetry”Bound into FeatlyTelemetryOptions — server-side OpenTelemetry. Off by
default. Config-only (not DB-overridable): the export pipeline is built once at
host startup, before the database is reachable.
The Featly meter (Featly.Server) and activity source (Featly.Server) are
always present and cost nothing while nothing listens. Enabled controls whether
AddFeatlyServerTelemetry(builder.Configuration) wires the OpenTelemetry SDK —
ASP.NET Core + HttpClient instrumentation plus the OTLP exporter.
| Key | Default | Notes |
|---|---|---|
Enabled |
false |
Master switch. When false, no OpenTelemetry services are registered and there is no per-request overhead. |
Traces |
true |
Export spans (HTTP server/client + Featly’s featly.change.apply, featly.webhook.deliver) when enabled. |
Metrics |
true |
Export meters (HTTP + Featly’s custom counters/histograms) when enabled. |
ServiceName |
Featly |
OpenTelemetry service.name resource attribute. |
OtlpEndpoint |
null |
OTLP collector URL (e.g. http://localhost:4317). When unset, falls back to OTEL_EXPORTER_OTLP_ENDPOINT and the OpenTelemetry default. |
OtlpProtocol |
Grpc |
Grpc (port 4317) or HttpProtobuf (port 4318). |
Custom metrics emitted by the server:
| Instrument | Type | Tags |
|---|---|---|
featly.server.evaluations |
counter | featly.entity_type (flag/config), featly.reason |
featly.server.events_ingested |
counter | featly.event_type (Exposure/Custom) |
featly.server.changes_applied |
counter | featly.change_action, featly.bypassed |
featly.server.audit_writes |
counter | featly.action |
featly.server.webhook_deliveries |
counter | featly.result (success/failure) |
featly.server.webhook_delivery_duration |
histogram (ms) | featly.result |
Example appsettings.json
Section titled “Example appsettings.json”{ "Featly": { "Server": { "AdminApiKey": "set-via-secret", "SdkApiKey": "set-via-secret", "DefaultEnvironmentKey": "production", "Features": { "Experiments": false } }, "Storage": { "Sqlite": { "ConnectionString": "Data Source=/var/lib/featly/featly.db", "AutoMigrate": false } }, "Authorization": { "AutoProvisionMode": "Closed" }, "Audit": { "RetentionDays": 90 }, "ApprovalDefaults": { "Prod": { "Required": true, "MinApprovals": 2 } }, "Webhooks": { "MaxAttempts": 8 }, "Telemetry": { "Enabled": true, "OtlpEndpoint": "http://otel-collector:4317" } }}Keep secrets out of source control: use environment variables
(Featly__Server__AdminApiKey=...) or a secret store, not committed JSON.
CLI environment variables
Section titled “CLI environment variables”The featly CLI reads these when an explicit option is not passed:
| Variable | Used by | Falls back to |
|---|---|---|
FEATLY_SQLITE |
featly db * (offline) |
Data Source=featly.db |
FEATLY_SERVER_URL |
featly apikey / env / export / import / bootstrap-admin |
http://localhost:5080 |
FEATLY_API_KEY |
the online admin commands (not bootstrap-admin) |
— (required) |
API keys
Section titled “API keys”The static AdminApiKey / SdkApiKey are bootstrap shortcuts. For real,
auditable identities, mint keys bound to a user:
featly apikey generate --name ci --user you@example.com --scope AdminWriteA user-bound key authenticates over Authorization: Bearer and acts as that
user, so RBAC, the audit log, and approvals attribute the action to a real
person. See ADR-0023.