# Configuration

> Source: https://agilitycms.com/docs/dotNet/configuration

This guide covers all configuration options for Agility CMS .NET applications, including API settings, caching, environment variables, and multi-locale support.

## Configuration Files

### appsettings.json

The main configuration file with default settings:

```json
{
  "AppSettings": {
    "InstanceGUID": "",
    "SecurityKey": "",
    "FetchAPIKey": "",
    "PreviewAPIKey": "",
    "Locales": "en-us",
    "ChannelName": "website",
    "CacheInMinutes": 5
  },
  "CacheControl": {
    "MaxAgeSeconds": 30,
    "StaleWhileRevalidateSeconds": 86400,
    "StaleIfErrorSeconds": 86400
  }
}
```

### appsettings.local.json

Local development credentials (gitignored):

```json
{
  "AppSettings": {
    "InstanceGUID": "your-instance-guid",
    "SecurityKey": "your-security-key",
    "FetchAPIKey": "defaultlive.your-fetch-key",
    "PreviewAPIKey": "defaultpreview.your-preview-key"
  }
}
```

### appsettings.Development.json

Development-specific overrides:

```json
{
  "DetailedErrors": true,
  "AppSettings": {
    "CacheInMinutes": 0
  }
}
```

## AppSettings Reference

### Required Settings

| Setting | Description | Example |
|---------|-------------|---------|
| `InstanceGUID` | Your Agility CMS instance identifier | `abc123-def456-...` |
| `SecurityKey` | Used for preview mode authentication | `MySecretKey123` |
| `FetchAPIKey` | API key for published content | `defaultlive.abc...` |
| `PreviewAPIKey` | API key for draft content | `defaultpreview.xyz...` |

### Optional Settings

| Setting | Default | Description |
|---------|---------|-------------|
| `Locales` | `en-us` | Comma-separated locale codes |
| `ChannelName` | `website` | Agility CMS sitemap channel |
| `CacheInMinutes` | `5` | Server-side cache duration |
| `WebsiteName` | - | Site name for display |

### Where to Find API Keys

1. Log in to [Agility CMS](https://app.agilitycms.com)
2. Go to **Settings > API Keys**
3. Copy the required values:
   - **GUID** - Instance identifier
   - **Security Key** - For preview authentication
   - **Live API Key** - Starts with `defaultlive`
   - **Preview API Key** - Starts with `defaultpreview`

## CacheControl Settings

Control CDN caching behavior with stale-while-revalidate headers:

| Setting | Default | Description |
|---------|---------|-------------|
| `MaxAgeSeconds` | `30` | How long CDN serves fresh content |
| `StaleWhileRevalidateSeconds` | `86400` | Serve stale while revalidating (1 day) |
| `StaleIfErrorSeconds` | `86400` | Serve stale if origin is down (1 day) |

### How It Works

```
Cache-Control: public, max-age=30, stale-while-revalidate=86400, stale-if-error=86400
```

1. **First 30 seconds**: CDN serves cached content directly
2. **After 30 seconds**: CDN serves stale content while fetching fresh in background
3. **If origin fails**: CDN continues serving stale content for up to 1 day

### Disabling CDN Caching

CDN cache headers are automatically disabled for:
- Preview mode requests
- Development environment

## Environment Variables

For production deployments (Azure, Docker, etc.), use environment variables instead of config files.

### Azure App Service Format

Use double underscores (`__`) for nested properties:

```bash
AppSettings__InstanceGUID=your-guid
AppSettings__SecurityKey=your-key
AppSettings__FetchAPIKey=defaultlive.your-key
AppSettings__PreviewAPIKey=defaultpreview.your-key
AppSettings__Locales=en-us,fr-ca
AppSettings__ChannelName=website
AppSettings__CacheInMinutes=5
CacheControl__MaxAgeSeconds=30
CacheControl__StaleWhileRevalidateSeconds=86400
CacheControl__StaleIfErrorSeconds=86400
```

### Setting in Azure Portal

1. Go to your App Service
2. Navigate to **Settings > Environment variables**
3. Add each setting as an application setting
4. Click **Save** and restart the app

### Using Azure CLI

```bash
az webapp config appsettings set \
  --name your-app-name \
  --resource-group your-resource-group \
  --settings \
    AppSettings__InstanceGUID=your-guid \
    AppSettings__SecurityKey=your-key \
    AppSettings__FetchAPIKey=defaultlive.your-key \
    AppSettings__PreviewAPIKey=defaultpreview.your-key
```

### Docker Environment Variables

```dockerfile
ENV AppSettings__InstanceGUID=your-guid
ENV AppSettings__SecurityKey=your-key
ENV AppSettings__FetchAPIKey=defaultlive.your-key
ENV AppSettings__PreviewAPIKey=defaultpreview.your-key
```

Or with docker-compose:

```yaml
services:
  web:
    environment:
      - AppSettings__InstanceGUID=your-guid
      - AppSettings__SecurityKey=your-key
      - AppSettings__FetchAPIKey=defaultlive.your-key
      - AppSettings__PreviewAPIKey=defaultpreview.your-key
```

## Multi-Locale Configuration

### Configuring Locales

Specify supported locales as a comma-separated list:

```json
{
  "AppSettings": {
    "Locales": "en-us,fr-ca,es-mx"
  }
}
```

### Locale Detection

The starters detect locale from:

1. **URL prefix** - `/fr-ca/about` uses `fr-ca` locale
2. **Default locale** - First locale in the list is default

### Locale-Specific Content

```csharp
// Fetch content for a specific locale
var posts = await _fetchApi.GetTypedContentList<Post>(
    referenceName: "posts",
    locale: "fr-ca",  // French Canadian
    take: 10
);
```

### Locale Switching

Implement locale switching in your header:

```razor
@foreach (var locale in Locales)
{
    <a href="/@locale@CurrentPath"
       class="@(locale == CurrentLocale ? "active" : "")">
        @GetLocaleDisplayName(locale)
    </a>
}
```

## Caching Configuration

### Server-Side Cache

The `CacheInMinutes` setting controls how long API responses are cached in memory:

| Environment | Recommended Value |
|-------------|-------------------|
| Development | `0` (disabled) |
| Staging | `1-2` |
| Production | `5-10` |

```json
{
  "AppSettings": {
    "CacheInMinutes": 5
  }
}
```

### Cache Behavior

- **CacheInMinutes = 0**: Every request fetches fresh data
- **CacheInMinutes > 0**: Data cached for specified duration
- **Preview mode**: Caching always disabled

### Cache Invalidation

Configure webhooks in Agility CMS to invalidate cache on publish:

1. Go to **Settings > Webhooks** in Agility CMS
2. Add webhook URL: `https://your-site.com/api/revalidate`
3. Select events to trigger invalidation

## Preview Mode Configuration

### How Preview Works

1. **Development**: Preview is always enabled
2. **Production**: Preview requires authentication via query parameter

### Preview URL Format

```
https://your-site.com/page-path?agilitypreviewkey=HASH
```

The `agilitypreviewkey` is generated from your `SecurityKey`.

### Setting Up Preview in Agility CMS

1. Go to **Settings > Preview URLs**
2. Add your site URL with the preview parameter placeholder:
   ```
   https://your-site.com{path}?agilitypreviewkey={previewkey}
   ```

### Custom Preview Key Validation

```csharp
public static bool ValidatePreviewKey(string key, string securityKey)
{
    using var sha256 = SHA256.Create();
    var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(securityKey));
    var expectedKey = Convert.ToBase64String(hash);
    return key == expectedKey;
}
```

## Reading Configuration

### In Program.cs / Startup.cs

```csharp
var builder = WebApplication.CreateBuilder(args);

// Access configuration
var instanceGuid = builder.Configuration["AppSettings:InstanceGUID"];
var locales = builder.Configuration["AppSettings:Locales"]?.Split(',') ?? new[] { "en-us" };

// Bind to strongly-typed options
builder.Services.Configure<AppSettings>(
    builder.Configuration.GetSection("AppSettings"));
```

### In Services (Dependency Injection)

```csharp
public class MyService
{
    private readonly AppSettings _settings;

    public MyService(IOptions<AppSettings> options)
    {
        _settings = options.Value;
    }

    public string GetInstanceGuid() => _settings.InstanceGUID;
}
```

### In Razor Components/Pages

```razor
@inject IOptions<AppSettings> AppSettings

@code {
    private string InstanceGuid => AppSettings.Value.InstanceGUID;
}
```

## Configuration Classes

### AppSettings Class

```csharp
public class AppSettings
{
    public string InstanceGUID { get; set; } = "";
    public string SecurityKey { get; set; } = "";
    public string FetchAPIKey { get; set; } = "";
    public string PreviewAPIKey { get; set; } = "";
    public string Locales { get; set; } = "en-us";
    public string ChannelName { get; set; } = "website";
    public int CacheInMinutes { get; set; } = 5;
    public string? WebsiteName { get; set; }

    public string[] GetLocales() => Locales.Split(',', StringSplitOptions.RemoveEmptyEntries);
    public string DefaultLocale => GetLocales().FirstOrDefault() ?? "en-us";
}
```

### CacheControl Class

```csharp
public class CacheControl
{
    public int MaxAgeSeconds { get; set; } = 30;
    public int StaleWhileRevalidateSeconds { get; set; } = 86400;
    public int StaleIfErrorSeconds { get; set; } = 86400;
}
```

## Security Best Practices

### Never Commit Secrets

1. Use `appsettings.local.json` for local development (gitignored)
2. Use environment variables for production
3. Never commit API keys to version control

### Verify .gitignore

Ensure your `.gitignore` includes:

```
appsettings.local.json
appsettings.*.local.json
*.local.json
```

### Use Azure Key Vault (Production)

For enhanced security, use Azure Key Vault:

```csharp
builder.Configuration.AddAzureKeyVault(
    new Uri($"https://{keyVaultName}.vault.azure.net/"),
    new DefaultAzureCredential()
);
```

## Troubleshooting

### "InstanceGUID is required"

1. Check `appsettings.local.json` exists
2. Verify the JSON is valid
3. Confirm the value is not empty

### "Invalid API Key"

1. Verify the key is copied correctly (no extra spaces)
2. Check you're using the correct key (live vs preview)
3. Regenerate the key in Agility CMS if needed

### Cache Not Invalidating

1. Verify webhook URL is correct
2. Check webhook is receiving events (Agility logs)
3. Confirm `CacheInMinutes` > 0

### Preview Mode Not Working

1. Verify `SecurityKey` matches Agility settings
2. Check the preview URL format
3. Clear browser cookies

## Next Steps

- [Deploy to Azure](/docs/dotNet/deploy-to-azure) - Production deployment
- [Fetching Content](/docs/dotNet/fetching-content) - API usage
- [Getting Started](/docs/dotNet/getting-started) - Initial setup
