Configuration Reference
Complete reference for appsettings.json settings in the Riptide Application Manager. Environment variables can override any setting — see Deployment Guide for environment-specific configuration.
Distinct from the API Reference, which documents the REST surface AM exposes to client applications. This document covers the configuration surface AM consumes at startup.
Which file, and in which process
Application Manager runs as two processes, and each reads its own configuration the
standard .NET way — appsettings.json and appsettings.{Environment}.json from its own
project, then environment variables:
| Process | Settings file |
|---|---|
| API | Riptide.ApplicationManager.Api/appsettings.json |
| Web UI | Riptide.ApplicationManager.Web/appsettings.json |
There is no single unified settings file. An earlier arrangement kept one at the repository
root and loaded it with custom code in both Program.cs files; that is being withdrawn.
It loaded from the binary directory rather than the content root, which put it above
appsettings.{Environment}.json and silently disabled the environment overlay — a
Development run produced no debug logging at all, because the base file's
Logging:LogLevel:Default won.
Settings each process must agree on — the platform key, the database stores — are therefore written in both files, deliberately and identically. Anything genuinely per-deployment comes from environment variables, which outrank every settings file.
Ports and Hosting
Ports come from the standard top-level Kestrel section, in each project's own file.
There is no Api:Kestrel or Web:Kestrel: Kestrel binds Kestrel:* and nothing else, so a
prefixed variant is well-formed, plausible, and read by nobody. Earlier revisions of this
document described exactly that, which is why the keys existed.
In the container neither file supplies the port — docker-entrypoint.sh sets
ASPNETCORE_URLS per process from API_PORT and WEB_PORT.
Web UI
{
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://*:11401" }
}
},
"Web": {
"ApiBaseUrl": "http://localhost:11402",
"BaseUrl": "http://localhost:11401",
"Timeout": 30
}
}
| Setting | Default | Description |
|---|---|---|
Kestrel:Endpoints:Http:Url |
http://*:11401 |
Web UI listen address (Web project's file) |
Web:ApiBaseUrl |
http://localhost:11402 |
Internal URL the Web UI uses to call the API. Required — ApiClient throws if absent rather than defaulting |
Web:BaseUrl |
http://localhost:11401 |
Browser-facing address the Web UI advertises |
Web:Timeout |
30 |
HTTP client timeout in seconds |
Authentication of Web UI → API calls uses Riptide:PlatformKey, one value shared by both
processes. Web:ApiKey and Api:ApiKey were the previous spellings and are read by nothing.
REST API
{
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://*:11402" }
}
},
"Riptide": {
"PlatformKey": ""
},
"Api": {
"Cors": {
"AllowedOrigins": ["http://localhost:11401"]
},
"MaxRequestBodySize": 10485760,
"RequestTimeout": 30
}
}
| Setting | Default | Description |
|---|---|---|
Kestrel:Endpoints:Http:Url |
http://*:11402 |
API listen address (API project's file) |
Api:Cors:AllowedOrigins |
["http://localhost:11401"] |
Allowed CORS origins |
Riptide:PlatformKey |
(none) | The platform key this API validates. Callers present it as X-Riptide-Key; the Web tier presents the same value. Replaces Api:ApiKey, which is read by nothing |
Api:MaxRequestBodySize |
10485760 (10 MB) |
Maximum request body size |
Api:RequestTimeout |
30 |
Request timeout in seconds |
Databases
{
"Database": {
"Identity": {
"Provider": "Sqlite",
"ConnectionString": "Data Source=data/identity/identity.db",
"MigrationsAssembly": "Riptide.ApplicationManager.Infrastructure"
},
"Configuration": {
"Provider": "Sqlite",
"ConnectionString": "Data Source=data/configuration/configuration.db",
"MigrationsAssembly": "Riptide.ApplicationManager.Infrastructure"
}
}
}
Application Manager uses two separate databases:
| Database | Purpose |
|---|---|
| Identity | Trial users, sessions, roles, capabilities, identity providers |
| Configuration | Admin users, managed applications, file tree, versions, audit logs |
The Provider field selects the relational engine per database and accepts
Sqlite (default), Postgres / PostgreSql, or SqlServer (aliases such as
npgsql, mssql are also accepted). An unrecognized value fails fast at
startup. SQLite stores single-file databases under data/; for PostgreSQL or
SQL Server, set ConnectionString to the appropriate engine connection string.
Migrations for the non-SQLite providers are handled separately — see
persistence-provider-switching-plan.md.
{
"Database": {
"Identity": {
"Provider": "Postgres",
"ConnectionString": "Host=db;Database=identity;Username=am;Password=..."
}
}
}
Secrets via Azure Key Vault
Application Manager can source secrets from Azure Key Vault, layered on top of
appsettings.json and environment variables (the vault wins for keys it
provides). This is opt-in:
{
"Azure": {
"KeyVault": {
"Enabled": true,
"VaultUri": "https://my-vault.vault.azure.net/"
}
}
}
| Key | Description |
|---|---|
Azure:KeyVault:Enabled |
true to enable Key Vault secret sourcing (default false). |
Azure:KeyVault:VaultUri |
The vault URI. Required when enabled; a missing/invalid URI is surfaced as a startup skip reason. |
Authentication uses DefaultAzureCredential: a managed identity when AM runs on
Azure, or a service principal via AZURE_TENANT_ID / AZURE_CLIENT_ID /
AZURE_CLIENT_SECRET on a self-hosted host. The principal needs the Key Vault
Secrets User role (or a get/list secrets access policy). See
azure-key-vault-secrets.md.
Web UI Settings
{
"Web": {
"Ui": {
"ApplicationName": "Riptide Application Manager",
"PageSize": 20,
"Theme": "light",
"EnableDebugInfo": false
},
"Session": {
"TimeoutMinutes": 60,
"SlidingExpiration": true
},
"Features": {
"EnableTrialManagement": true,
"EnableConfigurationManagement": true,
"EnableFileVersioning": true,
"EnableAuditLog": true
}
}
}
| Setting | Default | Description |
|---|---|---|
Web:Ui:ApplicationName |
Riptide Application Manager |
Display name in the UI header |
Web:Ui:PageSize |
20 |
Default page size for list views |
Web:Ui:Theme |
light |
UI theme |
Web:Ui:EnableDebugInfo |
false |
Show debug information in the UI |
Web:Session:TimeoutMinutes |
60 |
Session inactivity timeout |
Web:Session:SlidingExpiration |
true |
Reset timeout on activity |
Web:Features:Enable* |
true |
Feature flags to enable/disable major sections |
Trial Settings
{
"Trial": {
"DefaultDurationDays": 7,
"GracePeriodDays": 30,
"MaxTeamMembers": 5,
"AutoActivate": true
}
}
| Setting | Default | Description |
|---|---|---|
Trial:DefaultDurationDays |
7 |
Default trial period length |
Trial:GracePeriodDays |
30 |
Grace period after trial expiration |
Trial:MaxTeamMembers |
5 |
Maximum team members per trial |
Trial:AutoActivate |
true |
Automatically activate trials after email verification |
{
"EmailProvider": "AwsSes",
"Email": {
"Enabled": true,
"FromAddress": "noreply@riptide.solutions",
"FromName": "Riptide Application Manager"
}
}
Set EmailProvider to either Smtp or AwsSes.
SMTP
{
"Smtp": {
"Host": "smtp.example.com",
"Port": 587,
"Username": "",
"Password": "",
"EnableSsl": true
}
}
AWS SES
{
"AwsSes": {
"Region": "us-east-1",
"AccessKey": "",
"SecretKey": "",
"ConfigurationSetName": ""
}
}
See the Deployment Guide for detailed AWS SES setup including IAM policies.
Security
{
"Security": {
"SessionTimeoutMinutes": 60,
"PasswordResetTokenExpirationMinutes": 60,
"MaxLoginAttempts": 5,
"LockoutDurationMinutes": 15
}
}
| Setting | Default | Description |
|---|---|---|
Security:SessionTimeoutMinutes |
60 |
Session timeout |
Security:PasswordResetTokenExpirationMinutes |
60 |
Password reset token lifetime |
Security:MaxLoginAttempts |
5 |
Failed attempts before lockout |
Security:LockoutDurationMinutes |
15 |
Lockout duration after max attempts |
SDK Security Middleware
{
"Riptide": {
"Security": {
"Headers": {
"EnableHsts": false,
"RemoveServerHeader": true,
"EnableXContentTypeOptions": true,
"EnableXFrameOptions": true,
"ContentSecurityPolicy": "default-src 'self'; ...",
"ReferrerPolicy": "strict-origin-when-cross-origin"
},
"Audit": {
"Enabled": true,
"StorageProvider": "Database",
"RetentionDays": 2555,
"IncludeRequestBody": false,
"IncludeResponseBody": false,
"ExcludePaths": ["/health", "/ready", "/hangfire"],
"AlertOnPolicyViolations": false
},
"Compliance": {
"Enabled": true,
"EnabledTemplates": ["SOC2", "HIPAA", "FedRAMP"]
}
}
}
}
| Setting | Default | Description |
|---|---|---|
Headers:EnableHsts |
false |
Enable HTTP Strict Transport Security (enable in production behind TLS) |
Headers:RemoveServerHeader |
true |
Remove the Server response header |
Audit:RetentionDays |
2555 |
Audit log retention (~7 years) |
Audit:ExcludePaths |
["/health", "/ready", "/hangfire"] |
Paths excluded from audit logging |
Compliance:EnabledTemplates |
["SOC2", "HIPAA", "FedRAMP"] |
Active compliance frameworks |
Scheduled Audits
{
"SecurityAudit": {
"ScheduledAudit": {
"Enabled": false,
"IntervalHours": 24,
"Frameworks": ["SOC2", "HIPAA", "FedRAMP", "StateRAMP"]
}
}
}
| Setting | Default | Description |
|---|---|---|
ScheduledAudit:Enabled |
false |
Enable automatic compliance audits |
ScheduledAudit:IntervalHours |
24 |
Hours between audit runs |
ScheduledAudit:Frameworks |
All four | Frameworks to audit |
Riptide SDK
{
"Riptide": {
"Logging": {
"ApplicationName": "RiptideApplicationManager",
"MinimumLevel": "Information",
"EnableCorrelationId": true,
"EnablePiiSanitization": true,
"Console": {
"Enabled": true,
"MinimumLevel": "Debug"
},
"File": {
"Enabled": true,
"LogDirectory": "logs",
"MaxFileSizeInMB": 10,
"RetainedFileCountLimit": 31
},
"External": {
"Enabled": false,
"Provider": "DataDog",
"ApiKey": ""
}
},
"Monitoring": {
"ApplicationName": "RiptideApplicationManager",
"EnableMetrics": true,
"EnableTracing": true,
"Provider": "Console",
"SamplingRate": 1.0
},
"Configuration": {
"Provider": "LocalDevelopment",
"EnableValidation": true,
"EnableCaching": true,
"CacheExpirationMinutes": 60
}
}
}
| Setting | Default | Description |
|---|---|---|
Logging:EnableCorrelationId |
true |
Add correlation IDs to all log entries |
Logging:EnablePiiSanitization |
true |
Sanitize personally identifiable information in logs |
Logging:File:RetainedFileCountLimit |
31 |
Number of log files to retain |
Monitoring:EnableMetrics |
true |
Enable performance metrics collection |
Monitoring:EnableTracing |
true |
Enable distributed tracing |
Monitoring:SamplingRate |
1.0 |
Trace sampling rate (1.0 = sample everything) |
Rate Limiting
{
"RateLimiting": {
"GlobalPermitLimit": 100,
"EmailResendLimitPerHour": 3,
"BulkOperationConcurrency": 3,
"ApiRequestsPerMinute": 60
}
}
File Management
{
"FileManagement": {
"MaxFileSizeBytes": 5242880,
"MaxVersions": 10,
"AllowedFileExtensions": [".json", ".yaml", ".yml", ".txt", ".xml", ".conf", ".config", ".env"]
}
}
| Setting | Default | Description |
|---|---|---|
MaxFileSizeBytes |
5242880 (5 MB) |
Maximum configuration file size |
MaxVersions |
10 |
Maximum versions retained per file |
AllowedFileExtensions |
See above | Permitted file types |
Background Jobs (Hangfire)
{
"Hangfire": {
"DashboardPath": "/hangfire",
"ServerName": "ApplicationManager",
"WorkerCount": 5,
"EnableDashboard": true
}
}
SignalR
{
"SignalR": {
"EnableDetailedErrors": true,
"MaximumReceiveMessageSize": 102400
}
}
The SignalR hub at /hubs/progress provides real-time progress updates for long-running operations like compliance assessments and bulk operations.
LLM Gateway
Configuration for the 2.0 AI/LLM gateway. The gateway routes agent and application LLM traffic to managed providers and meters it against budgets.
Gateway authentication
{
"Llm": {
"Gateway": {
"Auth": {
"AllowApiKey": true,
"AllowAgentBearer": true
}
}
}
}
| Key | Default | Description |
|---|---|---|
Llm:Gateway:Auth:AllowApiKey |
true |
Accept service-account API keys (X-Api-Key) on the gateway endpoints. |
Llm:Gateway:Auth:AllowAgentBearer |
true |
Accept agent bearer tokens (Authorization: Bearer) on the gateway endpoints. |
At least one of the two must be enabled or the API fails fast at startup.
Provider endpoint allowlist (SSRF guard)
Outbound provider calls are restricted to an allowlist of hosts. Configure the permitted provider hosts so the gateway cannot be coerced into calling arbitrary URLs:
{
"Llm": {
"ProviderEndpoints": {
"AllowedHosts": [
"api.openai.com",
"api.anthropic.com",
"*.openai.azure.com"
]
}
}
}
Provider accounts and API-key storage
Each provider account chooses where its API key lives (KeySource), set per
account in the Web UI under Provider Accounts:
KeySource |
Where the key is read from |
|---|---|
EnvironmentVariable (default) |
An environment variable named by the account's ApiKeySecretName (with a configuration fallback). |
Database |
Stored in the database, encrypted at rest via Data Protection; plaintext is never persisted or returned. |
Vault |
A named secret in the configured Azure Key Vault, resolved by ApiKeySecretName. |
Per-model limits (including an optional MaxOutputTokens cap) are configured on
each model-catalog entry through the Models screen, not in appsettings.json.
Environment Variable Overrides
Any setting can be overridden via environment variables using the standard ASP.NET Core convention (double underscores for section separators):
# Override the API key
Api__ApiKey=rtk_your-api-key-here
# Point the Identity store somewhere else. Provider and connection string belong to the
# same section — change one and change the other.
Riptide__Database__Identity__Provider=SQLite
Riptide__Database__Identity__ConnectionString="Data Source=/custom/path/identity.db"
# Enable scheduled audits
SecurityAudit__ScheduledAudit__Enabled=true
In Docker deployments, set these in the .env file or pass them directly to docker compose.