> ## Documentation Index
> Fetch the complete documentation index at: https://docs.deploystack.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Global Settings Management

> Comprehensive key-value store system with group organization, encryption support, and auto-initialization for DeployStack Backend configuration management.

This document describes the global key-value store system with group-based organization for managing application-wide configuration and credentials in DeployStack.

## Overview

The global settings system provides secure storage for application-wide configuration values organized into logical groups for better management and frontend display:

* **SMTP Mail Settings**: Host, port, username, password for email functionality
* **GitHub OAuth Configuration**: GitHub OAuth client ID and secret for authentication
* **API Keys**: External service credentials (OpenAI, AWS, etc.)
* **System Configuration**: Application-wide settings and feature flags
* **Integration Credentials**: Third-party service authentication tokens
* **Environment Variables**: Dynamic configuration that can be changed without code deployment

### Group-Based Organization

The system now uses **groups** to organize settings into logical categories that can be displayed as tabs in the frontend:

* **Group ID**: Technical identifier used for API queries (e.g., `smtp`)
* **Group Name**: Human-readable display name (e.g., `SMTP Mail Settings`)
* **Group Metadata**: Description, icon, and sort order for frontend display

### Auto-Initialization System

The system includes an **auto-initialization feature** that automatically creates missing groups and global settings when the server starts. Settings are defined in modular files within the `src/global-settings/` directory, and the system will:

* Scan for setting definition files on startup
* Create missing groups with metadata
* Check which settings exist in the database
* Create missing settings with default values (non-destructive)
* Link settings to their appropriate groups
* Preserve existing settings and their values
* Log initialization results for transparency

## Key Features

* **Group-Based Organization**: Settings organized into logical groups for frontend tabs
* **Hierarchical Keys**: Dot notation organization (e.g., `smtp.host`, `api.openai.key`)
* **Encryption Support**: Automatic encryption for sensitive values using AES-256-GCM
* **Group Metadata**: Display names, descriptions, icons, and sort order for groups
* **Admin-Only Access**: Only `global_admin` users can manage settings
* **Audit Trail**: Track setting changes with timestamps
* **Search Functionality**: Find settings by key patterns
* **Bulk Operations**: Create/update multiple settings at once
* **Health Monitoring**: Built-in encryption system health checks

## Security

### Encryption

Sensitive values are encrypted using industry-standard AES-256-GCM encryption:

* **Algorithm**: AES-256-GCM (Galois/Counter Mode)
* **Key Derivation**: Scrypt with fixed salt from `DEPLOYSTACK_ENCRYPTION_SECRET` environment variable
* **Authenticated Encryption**: Prevents tampering with encrypted data
* **Unique IVs**: Each encryption operation uses a unique initialization vector
* **Additional Authenticated Data**: Extra security layer to prevent manipulation

### Access Control

* **Role-Based Access**: Only users with `global_admin` role can access settings
* **Permission-Based**: Granular permissions for view, edit, and delete operations
* **Session Validation**: All requests require valid authentication

### Environment Variables

The system requires the `DEPLOYSTACK_ENCRYPTION_SECRET` environment variable:

```bash theme={null}
DEPLOYSTACK_ENCRYPTION_SECRET=your-very-secure-32-character-secret-key-here
```

**Important**: Use a strong, unique secret in production. This key is used to derive the encryption key for all sensitive settings.

### Security Guidelines

Follow these guidelines when working with sensitive settings:

* **Always encrypt sensitive data**: Passwords, API keys, tokens, and secrets must be encrypted
* **Use descriptive descriptions**: Help administrators understand what each setting does and why it's sensitive
* **Group sensitive settings**: Keep all sensitive settings for a service in the same group for easier management
* **Regular audits**: Review settings periodically for unused or outdated values
* **Environment separation**: Use different encryption secrets for different environments (development, staging, production)

## Database Schema

```sql theme={null}
-- Groups table for organizing settings
CREATE TABLE globalSettingGroups (
  id TEXT PRIMARY KEY,                     -- Group identifier (e.g., 'smtp')
  name TEXT NOT NULL,                      -- Display name (e.g., 'SMTP Mail Settings')
  description TEXT,                        -- Group description
  icon TEXT,                               -- Optional icon (lucide) for frontend 
  sort_order INTEGER DEFAULT 0 NOT NULL,  -- Display order for tabs
  created_at INTEGER NOT NULL,             -- Creation timestamp
  updated_at INTEGER NOT NULL              -- Last update timestamp
);

-- Settings table with group reference
CREATE TABLE globalSettings (
  key TEXT PRIMARY KEY,                    -- Setting identifier (e.g., 'smtp.host')
  value TEXT NOT NULL,                     -- Setting value (encrypted if sensitive)
  description TEXT,                        -- Human-readable description
  is_encrypted BOOLEAN NOT NULL DEFAULT FALSE, -- Whether value is encrypted
  group_id TEXT REFERENCES globalSettingGroups(id), -- Group reference
  created_at INTEGER NOT NULL,             -- Creation timestamp
  updated_at INTEGER NOT NULL              -- Last update timestamp
);
```

## Naming Conventions

When creating global settings, follow these conventions for consistency:

* **Dot notation for hierarchy**: Use `category.subcategory.setting` structure (e.g., `smtp.host`, `api.openai.key`)
* **Lowercase with underscores**: Use lowercase letters with underscores for readability (e.g., `smtp.max_retry_count`)
* **Be descriptive but concise**: Use clear names without redundancy (e.g., `api.openai.key` not `api.openai.api_key`)
* **Group related settings**: Keep related configuration together (e.g., `database.host`, `database.port`, `database.name`)
* **Hierarchical keys within groups**: Use dot notation for subcategories (e.g., `group.subcategory.setting`)
* **Keep all related settings in the same group**: Maintain group consistency for better organization
* **Provide clear descriptions**: Help administrators understand the purpose of each setting

## API Endpoints

### Authentication

All endpoints require authentication and appropriate permissions:

* **View Settings**: Requires `settings.view` permission
* **Create/Update Settings**: Requires `settings.edit` permission
* **Delete Settings**: Requires `settings.delete` permission

### Group Management

#### Get All Groups with Settings

```http theme={null}
GET /api/settings/groups
Authorization: Bearer <token>
```

**Response:**

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "smtp",
      "name": "SMTP Mail Settings",
      "description": "Email server configuration for sending notifications",
      "icon": "mail",
      "sort_order": 1,
      "settings": [
        {
          "key": "smtp.host",
          "value": "smtp.gmail.com",
          "description": "SMTP server hostname",
          "is_encrypted": false,
          "created_at": "2025-01-06T20:00:00.000Z",
          "updated_at": "2025-01-06T20:00:00.000Z"
        }
      ],
      "created_at": "2025-01-06T20:00:00.000Z",
      "updated_at": "2025-01-06T20:00:00.000Z"
    }
  ]
}
```

#### Get Specific Group

```http theme={null}
GET /api/settings/groups/:groupId
Authorization: Bearer <token>
```

#### Create Group

```http theme={null}
POST /api/settings/groups
Authorization: Bearer <token>
Content-Type: application/json

{
  "id": "api-keys",
  "name": "API Keys",
  "description": "External service API keys and credentials",
  "icon": "key",
  "sort_order": 3
}
```

#### Update Group

```http theme={null}
PUT /api/settings/groups/:groupId
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Updated Group Name",
  "description": "Updated description",
  "sort_order": 2
}
```

### Settings Management

#### List All Settings

```http theme={null}
GET /api/settings
Authorization: Bearer <token>
```

#### Get Settings by Group

```http theme={null}
GET /api/settings/group/:groupId
Authorization: Bearer <token>
```

**Example:**

```http theme={null}
GET /api/settings/group/smtp
```

#### Get Specific Setting

```http theme={null}
GET /api/settings/:key
Authorization: Bearer <token>
```

#### Create New Setting

```http theme={null}
POST /api/settings
Authorization: Bearer <token>
Content-Type: application/json

{
  "key": "smtp.password",
  "value": "secret123",
  "description": "SMTP server password",
  "encrypted": true,
  "group_id": "smtp"
}
```

#### Update Existing Setting

```http theme={null}
PUT /api/settings/:key
Authorization: Bearer <token>
Content-Type: application/json

{
  "value": "new-secret-value",
  "description": "Updated SMTP password",
  "encrypted": true,
  "group_id": "smtp"
}
```

#### Delete Setting

```http theme={null}
DELETE /api/settings/:key
Authorization: Bearer <token>
```

#### Search Settings

```http theme={null}
POST /api/settings/search
Authorization: Bearer <token>
Content-Type: application/json

{
  "pattern": "smtp"
}
```

#### Bulk Create/Update Settings

```http theme={null}
POST /api/settings/bulk
Authorization: Bearer <token>
Content-Type: application/json

{
  "settings": [
    {
      "key": "smtp.host",
      "value": "smtp.gmail.com",
      "group_id": "smtp"
    },
    {
      "key": "smtp.port",
      "value": "587",
      "group_id": "smtp"
    },
    {
      "key": "smtp.password",
      "value": "secret123",
      "encrypted": true,
      "group_id": "smtp"
    }
  ]
}
```

#### Health Check

```http theme={null}
GET /api/settings/health
Authorization: Bearer <token>
```

## Helper Methods (Recommended)

The GlobalSettings helper class provides simple, type-safe methods for retrieving setting values. These helpers are designed for common use cases where you just need the value of a setting, similar to the Email service helper methods.

### Import the Helper Class

```typescript theme={null}
import { GlobalSettings } from '../global-settings';
// or
import { GlobalSettings } from '../global-settings/helpers';
```

### Basic Usage

#### Get Setting Values

```typescript theme={null}
// Get a string value
const smtpHost = await GlobalSettings.get('smtp.host');
const smtpHostWithDefault = await GlobalSettings.get('smtp.host', 'localhost');

// Get typed values
const maxRetries = await GlobalSettings.getNumber('system.max_retries', 3);
const debugMode = await GlobalSettings.getBoolean('system.debug', false);
const apiUrl = await GlobalSettings.getUrl('api.base_url');
const supportEmail = await GlobalSettings.getEmail('support.email');

// Get required values (throws if missing)
const databaseUrl = await GlobalSettings.getRequired('database.url');
```

#### Type-Safe Getters

```typescript theme={null}
// Boolean values (accepts: 'true', 'false', '1', '0', 'yes', 'no', 'on', 'off', 'enabled', 'disabled')
const maintenanceMode = await GlobalSettings.getBoolean('system.maintenance_mode', false);
const emailEnabled = await GlobalSettings.getBoolean('email.enabled', true);

// Numeric values
const uploadLimit = await GlobalSettings.getNumber('upload.max_size_mb', 10);
const retryCount = await GlobalSettings.getInteger('api.retry_count', 3);

// URL validation
const webhookUrl = await GlobalSettings.getUrl('webhook.endpoint');
const callbackUrl = await GlobalSettings.getUrl('oauth.callback', 'http://localhost:3000/callback');

// Email validation
const adminEmail = await GlobalSettings.getEmail('admin.email');
const fromEmail = await GlobalSettings.getEmail('smtp.from_email', 'noreply@example.com');
```

#### Advanced Data Types

```typescript theme={null}
// JSON objects
interface ApiConfig {
  timeout: number;
  retries: number;
  endpoints: string[];
}
const apiConfig = await GlobalSettings.getJson<ApiConfig>('api.config');

// Arrays (comma-separated values)
const allowedDomains = await GlobalSettings.getArray('security.allowed_domains');
const adminEmails = await GlobalSettings.getArray('admin.emails', ['admin@example.com']);
```

### Batch Operations

#### Get Multiple Settings

```typescript theme={null}
// Get multiple settings at once
const settings = await GlobalSettings.getMultiple([
  'smtp.host',
  'smtp.port', 
  'smtp.username'
]);
// Returns: { 'smtp.host': 'smtp.gmail.com', 'smtp.port': '587', 'smtp.username': 'user@gmail.com' }

// Get all settings in a group (without group prefix)
const smtpConfig = await GlobalSettings.getGroupValues('smtp');
// Returns: { 'host': 'smtp.gmail.com', 'port': '587', 'username': 'user@gmail.com', ... }

// Get all settings in a group (with full keys)
const smtpSettings = await GlobalSettings.getGroupValuesWithFullKeys('smtp');
// Returns: { 'smtp.host': 'smtp.gmail.com', 'smtp.port': '587', 'smtp.username': 'user@gmail.com', ... }
```

### Utility Methods

#### Check Setting Status

```typescript theme={null}
// Check if setting exists and has a value
if (await GlobalSettings.isSet('smtp.host')) {
  logger.info('SMTP host is configured');
}

// Check if setting is empty
if (await GlobalSettings.isEmpty('api.key')) {
  logger.warn('API key needs to be configured');
}

// Check if setting exists in database (regardless of value)
if (await GlobalSettings.exists('feature.new_ui')) {
  logger.info('New UI feature flag exists');
}
```

#### Error Handling

```typescript theme={null}
try {
  // This will throw if the setting is missing or empty
  const requiredApiKey = await GlobalSettings.getRequired('api.secret_key');
  
  // Use the API key
  const response = await fetch('/api/data', {
    headers: { 'Authorization': `Bearer ${requiredApiKey}` }
  });
} catch (error) {
  logger.error('Required setting missing:', error.message);
  // Handle missing configuration
}
```

### Real-World Examples

#### SMTP Configuration

```typescript theme={null}
import { GlobalSettings } from '../global-settings';

// Simple approach using helpers
const smtpConfig = {
  host: await GlobalSettings.getRequired('smtp.host'),
  port: await GlobalSettings.getNumber('smtp.port', 587),
  secure: await GlobalSettings.getBoolean('smtp.secure', true),
  auth: {
    user: await GlobalSettings.getRequired('smtp.username'),
    pass: await GlobalSettings.getRequired('smtp.password'),
  },
  from: {
    name: await GlobalSettings.get('smtp.from_name', 'DeployStack'),
    address: await GlobalSettings.get('smtp.from_email') || await GlobalSettings.getRequired('smtp.username'),
  }
};

// Or get all SMTP settings at once
const smtpSettings = await GlobalSettings.getGroupValues('smtp');
const smtpConfigFromGroup = {
  host: smtpSettings.host,
  port: parseInt(smtpSettings.port || '587'),
  secure: smtpSettings.secure === 'true',
  auth: {
    user: smtpSettings.username,
    pass: smtpSettings.password,
  }
};
```

#### Feature Flags

```typescript theme={null}
// Check feature flags
const features = {
  newDashboard: await GlobalSettings.getBoolean('features.new_dashboard', false),
  apiV2: await GlobalSettings.getBoolean('features.api_v2', false),
  debugMode: await GlobalSettings.getBoolean('system.debug', false),
  maintenanceMode: await GlobalSettings.getBoolean('system.maintenance', false),
};

if (features.maintenanceMode) {
  return res.status(503).json({ error: 'System under maintenance' });
}
```

#### API Configuration

```typescript theme={null}
// API service configuration
const apiConfig = {
  baseUrl: await GlobalSettings.getUrl('api.base_url', 'https://api.example.com'),
  timeout: await GlobalSettings.getNumber('api.timeout_ms', 30000),
  retries: await GlobalSettings.getInteger('api.max_retries', 3),
  apiKey: await GlobalSettings.getRequired('api.secret_key'),
  allowedOrigins: await GlobalSettings.getArray('api.allowed_origins', ['localhost']),
};

// Use in API client
const response = await fetch(`${apiConfig.baseUrl}/data`, {
  timeout: apiConfig.timeout,
  headers: {
    'Authorization': `Bearer ${apiConfig.apiKey}`,
    'Content-Type': 'application/json'
  }
});
```

#### System Configuration

```typescript theme={null}
// System-wide settings
const systemConfig = {
  maxUploadSize: await GlobalSettings.getNumber('system.max_upload_mb', 10),
  sessionTimeout: await GlobalSettings.getNumber('system.session_timeout_hours', 24),
  logLevel: await GlobalSettings.get('system.log_level', 'info'),
  adminEmails: await GlobalSettings.getArray('system.admin_emails'),
  supportEmail: await GlobalSettings.getEmail('system.support_email', 'support@example.com'),
};
```

## Usage Examples (GlobalSettingsService)

For more complex operations like creating, updating, or searching settings, use the GlobalSettingsService directly:

### SMTP Configuration

```typescript theme={null}
import { GlobalSettingsService } from '../services/globalSettingsService';

// Set SMTP configuration
await GlobalSettingsService.set('smtp.host', 'smtp.gmail.com', { 
  group_id: 'smtp',
  description: 'SMTP server hostname'
});

await GlobalSettingsService.set('smtp.port', '587', { 
  group_id: 'smtp',
  description: 'SMTP server port'
});

await GlobalSettingsService.set('smtp.username', 'user@gmail.com', { 
  group_id: 'smtp',
  description: 'SMTP authentication username'
});

await GlobalSettingsService.set('smtp.password', 'app-password', { 
  group_id: 'smtp',
  description: 'SMTP authentication password',
  encrypted: true
});

// Retrieve SMTP configuration by group
const smtpSettings = await GlobalSettingsService.getByGroup('smtp');
const smtpConfig = {
  host: smtpSettings.find(s => s.key === 'smtp.host')?.value,
  port: parseInt(smtpSettings.find(s => s.key === 'smtp.port')?.value || '587'),
  auth: {
    user: smtpSettings.find(s => s.key === 'smtp.username')?.value,
    pass: smtpSettings.find(s => s.key === 'smtp.password')?.value,
  }
};
```

### API Keys Management

```typescript theme={null}
// Store encrypted API keys
await GlobalSettingsService.set('api.openai.key', 'sk-...', { 
  group_id: 'api-keys',
  description: 'OpenAI API key for AI integrations',
  encrypted: true
});

await GlobalSettingsService.set('api.aws.access_key', 'AKIA...', { 
  group_id: 'api-keys',
  description: 'AWS access key for cloud services',
  encrypted: true
});

// Retrieve API configuration by group
const apiSettings = await GlobalSettingsService.getByGroup('api-keys');
const openaiKey = apiSettings.find(s => s.key === 'api.openai.key')?.value;
```

### Error Handling

When working with global settings, implement proper error handling:

```typescript theme={null}
try {
  const setting = await GlobalSettingsService.get('api.openai.key');
  if (!setting) {
    throw new Error('OpenAI API key not configured');
  }
  // Use the setting
} catch (error) {
  logger.error('Failed to retrieve setting:', error);
  // Handle the error appropriately
}
```

## Auto-Initialization System

### Overview

The auto-initialization system automatically creates missing groups and global settings when the server starts. This ensures that all required groups and settings are available without manual configuration, while preserving existing values.

### File-Based Setting Definitions

Settings are defined in TypeScript files within the `src/global-settings/` directory:

```text theme={null}
src/global-settings/
├── types.ts              # Type definitions
├── index.ts              # Auto-discovery service
├── smtp.ts               # SMTP configuration
├── github-oauth.ts       # GitHub OAuth settings
└── [custom].ts           # Your custom settings
```

### Setting Definition Format

Each setting file exports a `GlobalSettingsModule` with group metadata:

```typescript theme={null}
// src/global-settings/smtp.ts
import type { GlobalSettingsModule } from './types';

export const smtpSettings: GlobalSettingsModule = {
  group: {
    id: 'smtp',
    name: 'SMTP Mail Settings',
    description: 'Email server configuration for sending notifications',
    icon: 'mail',
    sort_order: 1
  },
  settings: [
    {
      key: 'smtp.host',
      name: 'SMTP Host',
      defaultValue: '',
      type: 'string',
      description: 'SMTP server hostname (e.g., smtp.gmail.com)',
      encrypted: false,
      required: true
    },
    {
      key: 'smtp.password',
      name: 'SMTP Password',
      defaultValue: '',
      type: 'string',
      description: 'SMTP authentication password',
      encrypted: true,
      required: true
    }
    // ... more settings
  ]
};
```

### Setting Properties

Each setting in the `settings` array requires these properties:

| Property       | Type                                | Required | Description                                                   |
| -------------- | ----------------------------------- | -------- | ------------------------------------------------------------- |
| `key`          | `string`                            | Yes      | Unique identifier using dot notation (e.g., `smtp.host`)      |
| `name`         | `string`                            | Yes      | Human-readable label for frontend display (e.g., `SMTP Host`) |
| `defaultValue` | `string \| number \| boolean`       | Yes      | Default value when setting is created                         |
| `type`         | `'string' \| 'number' \| 'boolean'` | Yes      | Value type for validation and UI rendering                    |
| `description`  | `string`                            | Yes      | Help text explaining what the setting does                    |
| `encrypted`    | `boolean`                           | Yes      | Whether to encrypt the value in the database                  |
| `required`     | `boolean`                           | Yes      | Whether the setting must have a non-empty value               |

### Startup Behavior

When the server starts:

1. **Discovery**: Scans `src/global-settings/` for `.ts` files
2. **Loading**: Dynamically imports each settings module
3. **Validation**: Ensures each module has the correct structure
4. **Group Creation**: Creates missing groups with metadata
5. **Database Check**: Checks which settings exist in the database
6. **Creation**: Creates missing settings with default values and group links
7. **Preservation**: Skips existing settings (non-destructive)
8. **Logging**: Reports initialization results

### Example Startup Output

```text theme={null}
🔄 Loading global settings definitions...
📁 Found 2 setting files: smtp, github-oauth
✅ Loaded settings module: smtp (7 settings)
✅ Loaded settings module: github-oauth (5 settings)
🎉 Loaded 2 settings modules with 12 total settings
🔄 Creating 2 setting groups...
✅ Created group: smtp (SMTP Mail Settings)
✅ Created group: github-oauth (GitHub OAuth Configuration)
🔄 Initializing 12 global settings...
✅ Created setting: smtp.host
✅ Created setting: smtp.port
✅ Created setting: smtp.username
✅ Created setting: smtp.password
⏭️  Skipped existing setting: smtp.secure
✅ Created setting: github.oauth.client_id
✅ Created setting: github.oauth.client_secret
🎉 Global settings initialization complete: 6 created, 1 skipped
⚠️  Missing required settings: smtp.host, smtp.username, smtp.password
```

### Built-in Setting Groups

#### SMTP Settings (Group ID: `smtp`)

**Source**: [services/backend/src/global-settings/smtp.ts](https://github.com/deploystackio/deploystack/blob/main/services/backend/src/global-settings/smtp.ts)

Email server configuration for sending notifications and system emails. Includes SMTP host, port, authentication credentials (encrypted password), SSL/TLS settings, and default sender information.

#### GitHub OAuth Settings (Group ID: `github-oauth`)

**Source**: [services/backend/src/global-settings/github-oauth.ts](https://github.com/deploystackio/deploystack/blob/main/services/backend/src/global-settings/github-oauth.ts)

GitHub OAuth application credentials for user authentication. Includes client ID, client secret (encrypted), enabled flag, callback URL, and requested OAuth scopes.

#### Global Settings (Group ID: `global`)

**Source**: [services/backend/src/global-settings/global.ts](https://github.com/deploystackio/deploystack/blob/main/services/backend/src/global-settings/global.ts)

Application-wide configuration settings. Controls frontend URL, email functionality toggle, login/registration enablement, and API documentation visibility.

#### User Display Settings (Group ID: `user-display`)

**Source**: [services/backend/src/global-settings/user-display.ts](https://github.com/deploystackio/deploystack/blob/main/services/backend/src/global-settings/user-display.ts)

Controls UI element visibility for users across the application. Manages header buttons like Discord community link and feedback form.

**Special behavior**: These settings are automatically included in the `/api/users/me` endpoint response, making them immediately available to the frontend without additional API calls.

### Helper Methods

The system provides helper methods for retrieving complete configurations:

```typescript theme={null}
import { GlobalSettingsInitService } from '../global-settings';

// Get complete SMTP configuration
const smtpConfig = await GlobalSettingsInitService.getSmtpConfiguration();
if (smtpConfig) {
  // Use smtpConfig.host, smtpConfig.port, etc.
}

// Get complete GitHub OAuth configuration
const githubConfig = await GlobalSettingsInitService.getGitHubOAuthConfiguration();
if (githubConfig && githubConfig.enabled) {
  // Use githubConfig.clientId, githubConfig.clientSecret, etc.
}

// Check if services are configured
const isSmtpReady = await GlobalSettingsInitService.isSmtpConfigured();
const isGitHubReady = await GlobalSettingsInitService.isGitHubOAuthConfigured();
```

### Adding New Setting Groups (Core)

To add a new core setting group (managed directly by the application):

1. **Create Setting File**: Add a new `.ts` file in `src/global-settings/`

```typescript theme={null}
// src/global-settings/my-service.ts
import type { GlobalSettingsModule } from './types';

export const myServiceSettings: GlobalSettingsModule = {
  group: {
    id: 'my-service',
    name: 'My Service Configuration',
    description: 'Settings for My Service integration',
    icon: 'service',
    sort_order: 3
  },
  settings: [
    {
      key: 'my-service.api_key',
      name: 'API Key',
      defaultValue: '',
      type: 'string',
      description: 'API key for My Service',
      encrypted: true,
      required: true
    },
    {
      key: 'my-service.enabled',
      name: 'Enable Service',
      defaultValue: false,
      type: 'boolean',
      description: 'Enable My Service integration',
      encrypted: false,
      required: false
    }
  ]
};
```

2. **Restart Server**: The new group and settings will be automatically discovered and initialized

3. **Add Helper Method** (optional): Add a helper method to `GlobalSettingsInitService`

```typescript theme={null}
// In src/global-settings/index.ts
static async getMyServiceConfiguration(): Promise<MyServiceConfig | null> {
  const settings = await GlobalSettingsService.getByGroup('my-service');
  
  const apiKey = settings.find(s => s.key === 'my-service.api_key')?.value;
  const enabled = settings.find(s => s.key === 'my-service.enabled')?.value;
  
  if (enabled !== 'true' || !apiKey) {
    return null;
  }
  
  return {
    apiKey,
    enabled: enabled === 'true'
  };
}
```

## Frontend Integration

The group-based system is designed for easy frontend integration:

### Dynamic Tab Creation

```typescript theme={null}
// Frontend can easily create tabs from groups
const response = await fetch('/api/settings/groups');
const { data: groups } = await response.json();

groups.forEach(group => {
  createTab({
    id: group.id,           // For routing: /settings/smtp
    label: group.name,      // Display: "SMTP Mail Settings"
    icon: group.icon,       // UI icon
    description: group.description,
    settings: group.settings, // Tab content
    sortOrder: group.sort_order
  });
});
```

### Group Management

```typescript theme={null}
// Get settings for a specific tab/group
const smtpSettings = await fetch('/api/settings/group/smtp');

// Update a setting within a group
await fetch('/api/settings/smtp.host', {
  method: 'PUT',
  body: JSON.stringify({
    value: 'new-smtp-host.com',
    group_id: 'smtp'
  })
});
```

### System Configuration

```typescript theme={null}
// System-wide feature flags and configuration
await GlobalSettingsService.set('system.maintenance_mode', 'false', { 
  group_id: 'system',
  description: 'Enable/disable maintenance mode'
});

await GlobalSettingsService.set('system.max_upload_size', '10485760', { 
  group_id: 'system',
  description: 'Maximum file upload size in bytes (10MB)'
});

await GlobalSettingsService.set('system.debug_logging', 'false', { 
  group_id: 'system',
  description: 'Enable debug logging'
});

// Check system configuration
const maintenanceMode = (await GlobalSettingsService.get('system.maintenance_mode'))?.value === 'true';
const maxUploadSize = parseInt((await GlobalSettingsService.get('system.max_upload_size'))?.value || '5242880');
```

## Group Design Guidelines

When designing setting groups for your application, follow these guidelines:

* **Logical grouping**: Group related settings together (e.g., all SMTP settings in one group)
* **Clear names**: Use descriptive group names that are clear for frontend display
* **Consistent icons**: Use consistent iconography across groups for better UX
* **Proper ordering**: Set `sort_order` values to control tab display sequence in the frontend

## Performance Considerations

### In-Memory Cache

The global settings system uses an **in-memory cache** to eliminate database queries for read operations. This is critical for high-frequency endpoints like health checks (`/`) and Swagger documentation (`/documentation`).

#### How It Works

```
┌─────────────────────────────────────────────────────────────┐
│                      Architecture                           │
├─────────────────────────────────────────────────────────────┤
│  Server Start → GlobalSettingsCache.load() → Memory Cache   │
│                                                             │
│  READ:  GlobalSettings.get() → Memory (instant, no DB)      │
│                                                             │
│  WRITE: GlobalSettingsService.set/update/delete()           │
│           → DB + Cache Invalidation (reload from DB)        │
└─────────────────────────────────────────────────────────────┘
```

#### Cache Lifecycle

1. **Startup**: After global settings initialization, all settings are loaded into memory
2. **Reads**: All `GlobalSettings.get*()` calls read from memory (no database query)
3. **Writes**: When settings are modified via `GlobalSettingsService`, the cache is automatically invalidated and reloaded
4. **Fallback**: If cache is not initialized, reads fall back to database queries

#### When Cache Is Invalidated

The cache automatically reloads from the database after:

* `GlobalSettingsService.set()` - Create or update a setting
* `GlobalSettingsService.setTyped()` - Create or update with type
* `GlobalSettingsService.update()` - Update an existing setting
* `GlobalSettingsService.delete()` - Delete a setting

#### Manual Cache Refresh

If you modify settings outside of `GlobalSettingsService` (not recommended), refresh the cache manually:

```typescript theme={null}
import { GlobalSettings } from '../global-settings';

await GlobalSettings.refreshCaches();
```

#### Single-Instance Limitation

The current cache implementation is designed for **single-instance deployments**. Multi-instance support (horizontal scaling) with shared cache synchronization is planned for a future release.

#### Monitoring Cache Status

```typescript theme={null}
import { GlobalSettingsCache } from '../global-settings';

// Check if cache is ready
if (GlobalSettingsCache.isInitialized()) {
  console.log(`Cache has ${GlobalSettingsCache.size()} settings`);
}
```

### Additional Performance Tips

* **Batch operations**: Use bulk endpoints when creating multiple related settings
* **Group retrieval**: Use `getGroupValues()` to fetch all settings in a group at once

## Migration and Setup

### Initial Setup

1. **Environment Variable**: Set `DEPLOYSTACK_ENCRYPTION_SECRET` in your environment
2. **Database Migration**: Run `npm run db:generate` and restart the server
3. **Admin Access**: Ensure you have a user with `global_admin` role

### Migrating from Category-Based System

The new group-based system replaces the old category-based approach. The migration is handled automatically:

1. **Database Migration**: The `category` column is renamed to `group_id`
2. **Auto-Initialization**: Groups are created automatically from setting definitions
3. **Setting Linking**: Existing settings are linked to appropriate groups

## Plugin-Contributed Global Settings

In addition to core global settings, plugins can also define and register their own global settings and setting groups. These are managed through the same system and are subject to the same access controls (i.e., editable by `global_admin`).

Key points for plugin-contributed settings:

* **Declaration**: Plugins declare global settings via a `globalSettingsExtension` property in their main class.
* **Initialization**: The `PluginManager` processes these definitions at startup, creating new groups and settings if they don't already exist.
* **Precedence**: Core global settings always take precedence. If a plugin tries to define a setting with a key that already exists (either from core or another plugin), the plugin's definition for that specific key is ignored.
* **Documentation**: For details on how plugins can define global settings, refer to the [Backend Plugins Docs](/development/backend/plugins) document.

## Helper Methods API Reference

### GlobalSettings Class Methods

| Method                                | Description                              | Returns                                   |
| ------------------------------------- | ---------------------------------------- | ----------------------------------------- |
| `get(key, defaultValue?)`             | Get a setting value as string            | `Promise<string \| null>`                 |
| `getString(key, defaultValue?)`       | Get a setting value as string (alias)    | `Promise<string \| null>`                 |
| `getBoolean(key, defaultValue?)`      | Get a setting value as boolean           | `Promise<boolean \| null>`                |
| `getNumber(key, defaultValue?)`       | Get a setting value as number            | `Promise<number \| null>`                 |
| `getInteger(key, defaultValue?)`      | Get a setting value as integer           | `Promise<number \| null>`                 |
| `getUrl(key, defaultValue?)`          | Get and validate setting as URL          | `Promise<string \| null>`                 |
| `getEmail(key, defaultValue?)`        | Get and validate setting as email        | `Promise<string \| null>`                 |
| `getJson<T>(key, defaultValue?)`      | Get and parse setting as JSON            | `Promise<T \| null>`                      |
| `getArray(key, defaultValue?)`        | Get setting as array (comma-separated)   | `Promise<string[]>`                       |
| `getRequired(key)`                    | Get required setting (throws if missing) | `Promise<string>`                         |
| `getMultiple(keys)`                   | Get multiple settings at once            | `Promise<Record<string, string \| null>>` |
| `getGroupValues(groupId)`             | Get group settings (without prefix)      | `Promise<Record<string, string \| null>>` |
| `getGroupValuesWithFullKeys(groupId)` | Get group settings (with full keys)      | `Promise<Record<string, string \| null>>` |
| `isSet(key)`                          | Check if setting exists and has value    | `Promise<boolean>`                        |
| `isEmpty(key)`                        | Check if setting is empty                | `Promise<boolean>`                        |
| `exists(key)`                         | Check if setting exists in database      | `Promise<boolean>`                        |
| `getRaw(key)`                         | Get raw setting object with metadata     | `Promise<GlobalSetting \| null>`          |
| `refreshCaches()`                     | Refresh any cached configurations        | `Promise<void>`                           |

### Boolean Value Parsing

The `getBoolean()` method accepts these string values:

| Value                                           | Result  |
| ----------------------------------------------- | ------- |
| `'true'`, `'1'`, `'yes'`, `'on'`, `'enabled'`   | `true`  |
| `'false'`, `'0'`, `'no'`, `'off'`, `'disabled'` | `false` |

### Usage Patterns

#### Simple Value Retrieval

```typescript theme={null}
const value = await GlobalSettings.get('key.name');
const valueWithDefault = await GlobalSettings.get('key.name', 'default');
```

#### Type-Safe Retrieval

```typescript theme={null}
const isEnabled = await GlobalSettings.getBoolean('feature.enabled', false);
const maxSize = await GlobalSettings.getNumber('upload.max_size', 10);
const apiUrl = await GlobalSettings.getUrl('api.endpoint');
```

#### Batch Retrieval

```typescript theme={null}
const settings = await GlobalSettings.getMultiple(['key1', 'key2', 'key3']);
const groupSettings = await GlobalSettings.getGroupValues('smtp');
```

#### Validation and Checks

```typescript theme={null}
if (await GlobalSettings.isSet('api.key')) {
  const apiKey = await GlobalSettings.getRequired('api.key');
}
```

## REST API Reference Summary

| Endpoint                        | Method | Permission        | Description                   |
| ------------------------------- | ------ | ----------------- | ----------------------------- |
| `/api/settings/groups`          | GET    | `settings.view`   | List all groups with settings |
| `/api/settings/groups/:groupId` | GET    | `settings.view`   | Get specific group            |
| `/api/settings/groups`          | POST   | `settings.edit`   | Create new group              |
| `/api/settings/groups/:groupId` | PUT    | `settings.edit`   | Update group                  |
| `/api/settings`                 | GET    | `settings.view`   | List all settings             |
| `/api/settings/:key`            | GET    | `settings.view`   | Get specific setting          |
| `/api/settings`                 | POST   | `settings.edit`   | Create new setting            |
| `/api/settings/:key`            | PUT    | `settings.edit`   | Update setting                |
| `/api/settings/:key`            | DELETE | `settings.delete` | Delete setting                |
| `/api/settings/group/:groupId`  | GET    | `settings.view`   | Get settings by group         |
| `/api/settings/search`          | POST   | `settings.view`   | Search settings               |
| `/api/settings/bulk`            | POST   | `settings.edit`   | Bulk create/update            |
| `/api/settings/health`          | GET    | `settings.view`   | System health check           |

***

For more information about the role-based access control system, see [ROLES](/development/backend/roles).
For security details, see [SECURITY](/development/backend/security).
