diff --git a/docs/development/backend/gateway-client-config.mdx b/docs/development/backend/gateway-client-config.mdx
new file mode 100644
index 0000000..06f42d4
--- /dev/null
+++ b/docs/development/backend/gateway-client-config.mdx
@@ -0,0 +1,78 @@
+---
+title: Gateway Client Configuration API
+description: Developer guide for the Gateway Client Configuration endpoint that provides pre-formatted configuration files for MCP clients to connect to the DeployStack Gateway
+---
+
+# Gateway Client Configuration API
+
+The Gateway Client Configuration API provides pre-formatted configuration files for various MCP clients to connect to the local DeployStack Gateway. This eliminates manual configuration steps and reduces setup errors for developers.
+
+## Overview
+
+The endpoint generates client-specific JSON configurations that users can directly use in their MCP clients (Claude Desktop, Cline, VSCode, Cursor, Windsurf) to connect to their local DeployStack Gateway running on `http://localhost:9095/sse`.
+
+## API Endpoint
+
+**Route:** `GET /api/gateway/config/:client`
+
+**Parameters:**
+- `:client` - The MCP client type (required)
+ - Supported values: `claude-desktop`, `cline`, `vscode`, `cursor`, `windsurf`
+
+**Authentication:** Dual authentication support
+- Cookie-based authentication (web users)
+- OAuth2 Bearer token authentication (CLI users)
+
+**Required Permission:** `gateway.config:read`
+**Required OAuth2 Scope:** `gateway:config:read`
+
+## Client Configurations
+
+The endpoint supports multiple MCP client types, each with its own optimized configuration format. All configurations use the DeployStack Gateway's SSE endpoint: `http://localhost:9095/sse`
+
+**Supported Clients:**
+- `claude-desktop` - Claude Desktop application
+- `cline` - Cline VS Code extension
+- `vscode` - VS Code MCP extension
+- `cursor` - Cursor IDE
+- `windsurf` - Windsurf AI IDE
+
+**Configuration Source:** For the current client configuration formats and JSON structures, see the `generateClientConfig()` function in:
+
+```bash
+services/backend/src/routes/gateway/config/get-client-config.ts
+```
+
+## Permission System
+
+### Role Assignments
+
+The `gateway.config:read` permission is assigned to:
+- **global_admin** - Basic access to get gateway configs they need
+- **global_user** - Basic access to get gateway configs they need
+
+### OAuth2 Scope
+
+The `gateway:config:read` OAuth2 scope:
+- **Purpose:** Enables CLI tools and OAuth2 clients to fetch gateway configurations
+- **Description:** "Generate client-specific gateway configuration files"
+- **Gateway Integration:** Automatically requested during gateway OAuth login
+
+## Future Enhancements
+
+### Additional Client Types
+- **Zed Editor** - Growing popularity in developer community
+- **Neovim** - Popular among command-line developers
+- **Custom Clients** - Generic JSON format for custom integrations
+
+### Configuration Customization
+- **Environment Variables** - Support for different gateway URLs
+- **TLS/SSL Support** - When gateway supports secure connections
+- **Authentication Tokens** - If gateway adds authentication in future
+
+## Related Documentation
+
+- [Backend API Security](/development/backend/api-security) - Security patterns and authorization
+- [Backend OAuth2 Server](/development/backend/oauth2-server) - OAuth2 implementation details
+- [Gateway OAuth Implementation](/development/gateway/oauth) - Gateway OAuth client
+- [Gateway SSE Transport](/development/gateway/sse-transport) - Gateway SSE architecture
diff --git a/docs/development/backend/index.mdx b/docs/development/backend/index.mdx
index 6b4cca0..12cb1eb 100644
--- a/docs/development/backend/index.mdx
+++ b/docs/development/backend/index.mdx
@@ -5,7 +5,7 @@ sidebar: Getting Started
---
import { Card, Cards } from 'fumadocs-ui/components/card';
-import { Database, Shield, Plug, Settings, Mail, TestTube, Wrench, BookOpen } from 'lucide-react';
+import { Database, Shield, Plug, Settings, Mail, TestTube, Wrench, BookOpen, Terminal } from 'lucide-react';
# DeployStack Backend Development
@@ -96,6 +96,14 @@ The development server starts at `http://localhost:3000` with API documentation
>
User roles, permissions system, and access control implementation details.
+
+ }
+ href="/deploystack/development/backend/gateway-client-config"
+ title="Gateway Client Configuration"
+ >
+ API endpoint for generating client-specific gateway configuration files with dual authentication support.
+
## Project Structure
diff --git a/docs/development/backend/mail.mdx b/docs/development/backend/mail.mdx
index e5501c7..54cebaf 100644
--- a/docs/development/backend/mail.mdx
+++ b/docs/development/backend/mail.mdx
@@ -51,17 +51,9 @@ The email system automatically integrates with your existing SMTP global setting
## Basic Usage
-### Import the Email Service
-
```typescript
import { EmailService } from '../email';
-// or
-import EmailService from '../email';
-```
-
-### Send a Basic Email
-```typescript
const result = await EmailService.sendEmail({
to: 'user@example.com',
subject: 'Welcome to DeployStack',
@@ -69,139 +61,40 @@ const result = await EmailService.sendEmail({
variables: {
userName: 'John Doe',
userEmail: 'user@example.com',
- loginUrl: 'https://app.deploystack.com/login',
- supportEmail: 'support@deploystack.com'
+ loginUrl: 'https://app.deploystack.io/login'
}
});
if (result.success) {
- request.log.info({
- messageId: result.messageId,
- recipients: result.recipients,
- operation: 'send_email'
- }, 'Email sent successfully');
+ request.log.info('Email sent successfully');
} else {
- request.log.error({
- error: result.error,
- recipients: result.recipients,
- operation: 'send_email'
- }, 'Failed to send email');
+ request.log.error('Failed to send email:', result.error);
}
```
-### Send to Multiple Recipients
-
-```typescript
-const result = await EmailService.sendEmail({
- to: ['user1@example.com', 'user2@example.com'],
- subject: 'System Maintenance Notice',
- template: 'notification',
- variables: {
- title: 'Scheduled Maintenance',
- message: 'We will be performing system maintenance on Sunday at 2 AM UTC.',
- actionUrl: 'https://status.deploystack.com',
- actionText: 'View Status Page'
- }
-});
-```
-
## Type-Safe Helper Methods
-The email service provides type-safe helper methods for common email types:
-
-### Welcome Email
+### Available Methods
+- `sendWelcomeEmail(options)` - Welcome email for new users
+- `sendPasswordResetEmail(options)` - Password reset instructions
+- `sendNotificationEmail(options)` - General notifications
```typescript
+// Example usage
const result = await EmailService.sendWelcomeEmail({
to: 'newuser@example.com',
userName: 'Jane Smith',
userEmail: 'newuser@example.com',
- loginUrl: 'https://app.deploystack.com/login',
- supportEmail: 'support@deploystack.com' // optional
-});
-```
-
-### Password Reset Email
-
-```typescript
-const result = await EmailService.sendPasswordResetEmail({
- to: 'user@example.com',
- userName: 'John Doe',
- resetUrl: 'https://app.deploystack.com/reset-password?token=abc123',
- expirationTime: '24 hours',
- supportEmail: 'support@deploystack.com' // optional
-});
-```
-
-### Notification Email
-
-```typescript
-const result = await EmailService.sendNotificationEmail({
- to: 'user@example.com',
- title: 'Deployment Complete',
- message: 'Your application has been successfully deployed to production.',
- actionUrl: 'https://app.deploystack.com/deployments/123',
- actionText: 'View Deployment',
- userName: 'John Doe' // optional
-});
-```
-
-## Advanced Usage
-
-### Custom From Address
-
-```typescript
-const result = await EmailService.sendEmail({
- to: 'user@example.com',
- subject: 'Custom Sender Example',
- template: 'notification',
- from: {
- name: 'DeployStack Notifications',
- email: 'notifications@deploystack.com'
- },
- variables: {
- title: 'Custom Message',
- message: 'This email is sent from a custom sender.'
- }
+ loginUrl: 'https://app.deploystack.io/login'
});
```
-### Email with Attachments
+## Advanced Features
-```typescript
-const result = await EmailService.sendEmail({
- to: 'user@example.com',
- subject: 'Report Attached',
- template: 'notification',
- variables: {
- title: 'Monthly Report',
- message: 'Please find your monthly deployment report attached.'
- },
- attachments: [
- {
- filename: 'report.pdf',
- content: reportBuffer,
- contentType: 'application/pdf'
- }
- ]
-});
-```
-
-### CC and BCC Recipients
-
-```typescript
-const result = await EmailService.sendEmail({
- to: 'primary@example.com',
- cc: ['manager@example.com'],
- bcc: ['audit@example.com'],
- subject: 'Important Update',
- template: 'notification',
- variables: {
- title: 'System Update',
- message: 'Important system update notification.'
- }
-});
-```
+- **Custom From Address**: Override default sender information
+- **Multiple Recipients**: Send to arrays of email addresses
+- **Attachments**: Include files with emails
+- **CC/BCC**: Additional recipient types supported
## Template System
@@ -221,97 +114,17 @@ All templates have access to these common variables:
- `appName`: Application name (default: 'DeployStack')
- `supportEmail`: Support email address (if provided)
-### Creating Custom Templates
-
-1. **Create a new Pug template** in `src/email/templates/`:
-
-```pug
-//- custom-template.pug
-//- @description Custom email template
-//- @variables customVar1, customVar2
-extends layouts/base.pug
-
-block content
- h1 Custom Email
-
- p Hello #{customVar1}!
-
- p= customVar2
-
- .text-center
- a.button(href="https://example.com") Take Action
-```
-
-2. **Add TypeScript types** in `src/email/types.ts`:
-
-```typescript
-export interface CustomEmailVariables {
- customVar1: string;
- customVar2: string;
-}
-
-// Add to TemplateVariableMap
-export interface TemplateVariableMap {
- welcome: WelcomeEmailVariables;
- 'password-reset': PasswordResetEmailVariables;
- notification: NotificationEmailVariables;
- 'custom-template': CustomEmailVariables; // Add this line
-}
-```
-
-3. **Use the custom template**:
-
-```typescript
-const result = await EmailService.sendEmail({
- to: 'user@example.com',
- subject: 'Custom Email',
- template: 'custom-template',
- variables: {
- customVar1: 'John',
- customVar2: 'This is a custom message.'
- }
-});
-```
-
-## Template Layout System
-
-### Base Layout
-
-The base layout (`layouts/base.pug`) provides:
-
-- Responsive HTML email structure
-- Cross-client CSS compatibility
-- Header and footer inclusion
-- Mobile-friendly design
-- Professional styling
-
-### Header Component
-
-The header (`layouts/header.pug`) displays:
+### Custom Templates
-- Application name/logo
-- Consistent branding
-- Professional appearance
+To create custom templates:
-### Footer Component
+1. Add new Pug template in `src/email/templates/`
+2. Add TypeScript types in `src/email/types.ts`
+3. Use with `EmailService.sendEmail()`
-The footer (`layouts/footer.pug`) includes:
+#### Color Guidelines
-- Copyright information
-- Contact information
-- Unsubscribe/support links
-- Legal disclaimers
-
-### Customizing Layouts
-
-To customize the layout, modify the files in `src/email/templates/layouts/`:
-
-```pug
-//- layouts/header.pug
-.header
- img(src="https://your-domain.com/logo.png" alt="Your Logo" style="height: 40px;")
- h1= appName || 'Your App Name'
-```
+When adding colors to email templates, follow the official DeployStack color system to maintain brand consistency. If you want to check what our primary colors are, visit the [UI Design System](/development/frontend/ui-design-system) page.
## Utility Methods
@@ -320,349 +133,50 @@ To customize the layout, modify the files in `src/email/templates/layouts/`:
```typescript
const status = await EmailService.testConnection(request.log);
if (status.success) {
- request.log.info({
- operation: 'test_smtp_connection'
- }, 'SMTP connection successful');
-} else {
- request.log.error({
- error: status.error,
- operation: 'test_smtp_connection'
- }, 'SMTP connection failed');
-}
-```
-
-### Check SMTP Configuration
-
-```typescript
-const status = await EmailService.getSmtpStatus(request.log);
-if (status.configured) {
- request.log.info({
- operation: 'check_smtp_status'
- }, 'SMTP is configured');
+ request.log.info('SMTP connection successful');
} else {
- request.log.error({
- error: status.error,
- operation: 'check_smtp_status'
- }, 'SMTP not configured');
+ request.log.error('SMTP connection failed:', status.error);
}
```
-### Refresh Configuration
+### Other Utilities
-```typescript
-// Call this after updating SMTP settings
-await EmailService.refreshConfiguration(request.log);
-```
-
-### Get Available Templates
-
-```typescript
-const templates = EmailService.getAvailableTemplates(request.log);
-request.log.info({
- templates,
- templateCount: templates.length,
- operation: 'get_available_templates'
-}, 'Available templates retrieved');
-// Output: ['welcome', 'password-reset', 'notification']
-```
-
-### Validate Template
-
-```typescript
-const validation = await EmailService.validateTemplate('welcome', {
- userName: 'John',
- userEmail: 'john@example.com',
- loginUrl: 'https://app.com/login'
-});
-
-if (validation.valid) {
- request.log.info({
- template: 'welcome',
- operation: 'validate_template'
- }, 'Template is valid');
-} else {
- request.log.error({
- template: 'welcome',
- errors: validation.errors,
- missingVariables: validation.missingVariables,
- operation: 'validate_template'
- }, 'Template validation failed');
-}
-```
+- `getSmtpStatus()` - Check SMTP configuration status
+- `refreshConfiguration()` - Reload SMTP configuration
+- `getAvailableTemplates()` - Get list of available templates
+- `validateTemplate()` - Validate template and variables
## Error Handling
-The email service provides comprehensive error handling:
-
-```typescript
-const result = await EmailService.sendEmail({
- to: 'invalid-email',
- subject: 'Test',
- template: 'welcome',
- variables: { userName: 'Test' }
-});
-
-if (!result.success) {
- switch (result.error) {
- case 'SMTP configuration is not available or invalid':
- // Handle SMTP configuration issues
- break;
- case 'Template \'welcome\' not found':
- // Handle missing template
- break;
- default:
- // Handle other errors
- request.log.error({
- error: result.error,
- operation: 'send_email'
- }, 'Email failed');
- }
-}
-```
-
-## Performance Considerations
-
-### Template Caching
-
-Templates are automatically cached after first compilation:
-
-```typescript
-// First call compiles and caches the template
-await EmailService.sendEmail({ template: 'welcome', ... });
-
-// Subsequent calls use cached template (faster)
-await EmailService.sendEmail({ template: 'welcome', ... });
-```
-
-### Clear Cache (Development)
-
-```typescript
-import { TemplateRenderer } from '../email';
-
-// Clear template cache during development
-TemplateRenderer.clearCache();
-```
-
-### Connection Pooling
-
-The email service uses connection pooling for better performance:
-
-- Maximum 5 concurrent connections
-- Maximum 100 messages per connection
-- Rate limiting: 5 emails per 20 seconds
-
-## Security Best Practices
+The email service provides comprehensive error handling with specific error messages for:
-### Input Validation
+- SMTP configuration issues
+- Missing templates
+- Invalid email addresses
+- Connection failures
-All email parameters are validated using Zod schemas:
+## Security & Performance
-```typescript
-// This will throw a validation error
-await EmailService.sendEmail({
- to: 'invalid-email-format', // ❌ Invalid email
- subject: '', // ❌ Empty subject
- template: '', // ❌ Empty template
- variables: {}
-});
-```
-
-### Template Security
-
-- Templates are compiled server-side (no client-side execution)
-- Variable injection is escaped by default
-- No arbitrary code execution in templates
-
-### SMTP Security
+### Security Features
+- Passwords encrypted in global settings
+- Secure connections (TLS/SSL) supported
+- Template security with escaped variable injection
-- Passwords are encrypted in global settings
-- Secure connections (TLS/SSL) are supported
-- Connection pooling with rate limiting
+### Performance Features
+- Template caching after first compilation
+- Connection pooling (max 5 concurrent connections)
+- Rate limiting (5 emails per 20 seconds)
-## Integration Examples
+## Common Usage Patterns
### User Registration
+Send welcome emails after user account creation.
-```typescript
-// In your user registration service
-import { EmailService } from '../email';
-
-export class UserService {
- static async registerUser(userData: UserRegistrationData) {
- // Create user account
- const user = await this.createUser(userData);
-
- // Send welcome email
- const emailResult = await EmailService.sendWelcomeEmail({
- to: user.email,
- userName: user.name,
- userEmail: user.email,
- loginUrl: `${process.env.FRONTEND_URL}/login`,
- supportEmail: 'support@deploystack.com'
- });
-
- if (!emailResult.success) {
- request.log.error({
- error: emailResult.error,
- userId: user.id,
- operation: 'send_welcome_email'
- }, 'Failed to send welcome email');
- // Don't fail registration if email fails
- }
-
- return user;
- }
-}
-```
-
-### Password Reset Flow
-
-```typescript
-// In your auth service
-import { EmailService } from '../email';
-
-export class AuthService {
- static async requestPasswordReset(email: string) {
- const user = await this.findUserByEmail(email);
- if (!user) {
- throw new Error('User not found');
- }
-
- // Generate reset token
- const resetToken = await this.generateResetToken(user.id);
- const resetUrl = `${process.env.FRONTEND_URL}/reset-password?token=${resetToken}`;
-
- // Send reset email
- const emailResult = await EmailService.sendPasswordResetEmail({
- to: user.email,
- userName: user.name,
- resetUrl,
- expirationTime: '24 hours',
- supportEmail: 'support@deploystack.com'
- });
-
- if (!emailResult.success) {
- throw new Error('Failed to send password reset email');
- }
-
- return { message: 'Password reset email sent' };
- }
-}
-```
-
-### Deployment Notifications
-
-```typescript
-// In your deployment service
-import { EmailService } from '../email';
-
-export class DeploymentService {
- static async notifyDeploymentComplete(deploymentId: string) {
- const deployment = await this.getDeployment(deploymentId);
- const user = await this.getUser(deployment.userId);
-
- const emailResult = await EmailService.sendNotificationEmail({
- to: user.email,
- title: 'Deployment Complete',
- message: `Your deployment "${deployment.name}" has been successfully completed.`,
- actionUrl: `${process.env.FRONTEND_URL}/deployments/${deploymentId}`,
- actionText: 'View Deployment',
- userName: user.name
- });
-
- if (!emailResult.success) {
- request.log.error({
- error: emailResult.error,
- deploymentId,
- userId: user.id,
- operation: 'send_deployment_notification'
- }, 'Failed to send deployment notification');
- }
- }
-}
-```
-
-## Troubleshooting
-
-### Common Issues
-
-1. **SMTP Configuration Not Found**
-
- ```text
- Error: SMTP configuration is not complete. Please configure SMTP settings in global settings.
- ```
-
- **Solution**: Configure SMTP settings in the global settings interface.
-
-2. **Template Not Found**
-
- ```text
- Error: Template 'welcome' not found at /path/to/templates/welcome.pug
- ```
-
- **Solution**: Ensure the template file exists in the templates directory.
-
-3. **Invalid Email Address**
-
- ```text
- Error: Invalid email address
- ```
-
- **Solution**: Validate email addresses before sending.
-
-4. **SMTP Connection Failed**
-
- ```text
- Error: Connection timeout
- ```
-
- **Solution**: Check SMTP server settings and network connectivity.
-
-### Debug Mode
-
-Enable debug logging for email operations:
-
-```typescript
-// Set environment variable
-process.env.DEBUG_EMAIL = 'true';
-
-// Or log email results
-const result = await EmailService.sendEmail({...}, request.log);
-request.log.debug({
- success: result.success,
- messageId: result.messageId,
- recipients: result.recipients,
- operation: 'send_email'
-}, 'Email result');
-```
-
-### Testing SMTP Configuration
-
-```typescript
-// Test SMTP connection before sending emails
-const connectionTest = await EmailService.testConnection(request.log);
-if (!connectionTest.success) {
- request.log.error({
- error: connectionTest.error,
- operation: 'test_smtp_connection'
- }, 'SMTP test failed');
- return;
-}
-
-// Proceed with sending emails
-const emailResult = await EmailService.sendEmail({...}, request.log);
-```
-
-## Best Practices
+### Password Reset
+Generate reset tokens and send secure reset links.
-1. **Always handle email failures gracefully** - Don't let email failures break your main application flow
-2. **Use type-safe helper methods** when possible for better developer experience
-3. **Test email templates** in different email clients for compatibility
-4. **Monitor email delivery** and set up alerts for failures
-5. **Use meaningful subject lines** and clear call-to-action buttons
-6. **Respect user preferences** for email notifications
-7. **Keep templates simple** and mobile-friendly
-8. **Cache templates** in production for better performance
+### System Notifications
+Alert users about deployments, system updates, or important events.
## API Reference
@@ -692,4 +206,4 @@ const emailResult = await EmailService.sendEmail({...}, request.log);
---
-For more information about global settings configuration, see [GLOBAL_SETTINGS](/development/backend/global-settings).
+For more information about global settings configuration, see [Global Settings](/development/backend/global-settings).
diff --git a/docs/development/backend/oauth2-server.mdx b/docs/development/backend/oauth2-server.mdx
index baeb7b8..3c7494c 100644
--- a/docs/development/backend/oauth2-server.mdx
+++ b/docs/development/backend/oauth2-server.mdx
@@ -154,13 +154,7 @@ For complete API specifications including request parameters, response schemas,
### Available Scopes
-Fine-grained permissions for API access:
-
-- `mcp:read` - Read MCP server configurations
-- `account:read` - Read account information
-- `user:read` - Read user profile
-- `teams:read` - Read team memberships
-- `offline_access` - Refresh token support
+For the current list of supported scopes, see the source code at `services/backend/src/services/oauth/authorizationService.ts` in the `validateScope()` method.
### Scope Enforcement
@@ -321,6 +315,75 @@ Comprehensive logging for debugging:
- Error conditions
- Security events
+## OAuth Scope Management
+
+The backend validates OAuth scopes to control API access. Scope configuration must stay synchronized between the backend and gateway.
+
+### Current Scopes
+
+For the current list of supported scopes, check the source code at:
+- **Backend validation**: `services/backend/src/services/oauth/authorizationService.ts` in the `validateScope()` method
+- **Gateway requests**: `services/gateway/src/utils/auth-config.ts` in the `scopes` array
+
+### Adding New Scopes
+
+When adding support for a new OAuth scope in the backend:
+
+1. **Add the scope** to the `allowedScopes` array in `services/backend/src/services/oauth/authorizationService.ts`
+2. **Update the gateway** to request the new scope (see [Gateway OAuth Implementation](/development/gateway/oauth))
+3. **Apply scope enforcement** to relevant API endpoints using middleware
+4. **Test the complete flow** to ensure proper scope validation
+
+Example:
+```typescript
+// In services/backend/src/services/oauth/authorizationService.ts
+static validateScope(scope: string): boolean {
+ const requestedScopes = scope.split(' ');
+ const allowedScopes = [
+ 'mcp:read',
+ 'mcp:categories:read',
+ 'your-new-scope', // Add new scope here
+ // ... other scopes
+ ];
+
+ return requestedScopes.every(s => allowedScopes.includes(s));
+}
+```
+
+### Scope Enforcement in Routes
+
+Apply scope requirements to API endpoints:
+
+```typescript
+// Single scope requirement
+server.get('/api/your-endpoint', {
+ preValidation: [
+ requireValidAccessToken(),
+ requireOAuthScope('your-new-scope')
+ ]
+}, async (request, reply) => {
+ // Your endpoint logic
+});
+
+// Multiple scope options
+server.get('/api/another-endpoint', {
+ preValidation: [
+ requireValidAccessToken(),
+ requireAnyOAuthScope(['scope1', 'scope2'])
+ ]
+}, async (request, reply) => {
+ // Your endpoint logic
+});
+```
+
+### Scope Synchronization
+
+**Critical**: The backend and gateway must have matching scope configurations:
+- If backend supports a scope but gateway doesn't request it, users won't get that permission
+- If gateway requests a scope but backend doesn't support it, authentication will fail
+
+Always coordinate scope changes between both services.
+
## Gateway Integration
The OAuth2 server integrates with the DeployStack Gateway:
diff --git a/docs/development/backend/user-preferences-system.mdx b/docs/development/backend/user-preferences-system.mdx
new file mode 100644
index 0000000..b531dbf
--- /dev/null
+++ b/docs/development/backend/user-preferences-system.mdx
@@ -0,0 +1,288 @@
+---
+title: User Preferences System
+description: Developer guide for managing user preferences in DeployStack Backend - adding new preferences, using the service layer, and understanding the architecture.
+---
+
+# User Preferences System
+
+The User Preferences System provides a flexible, config-driven approach to managing user-specific settings and behavioral data in DeployStack. This system handles everything from onboarding states to UI preferences without requiring database migrations for new preferences.
+
+## System Overview
+
+We use a separate table architecture where each preference is stored as an individual row, providing excellent queryability and performance. The system is designed around three core principles:
+
+- **Config-Driven**: New preferences require only configuration changes, no database migrations
+- **Type-Safe**: Full TypeScript integration with runtime validation
+- **Self-Service**: Users manage only their own preferences with proper security isolation
+
+## Architecture Components
+
+### Configuration Layer
+All preferences are defined in `/src/config/user-preferences.ts` as the single source of truth. This file contains default values and type definitions for all available preferences.
+
+### Service Layer
+The `UserPreferencesService` handles all database operations, type conversion, and business logic for preference management.
+
+### API Layer
+RESTful endpoints provide secure access to preferences with permission-based authorization.
+
+### Database Layer
+The `userPreferences` table stores individual key-value pairs with proper indexing for performance.
+
+## Adding New Preferences
+
+Adding a new preference is remarkably simple and requires no database migrations.
+
+### Step 1: Update Configuration
+
+Edit `/src/config/user-preferences.ts` and add your new preference to the `DEFAULT_USER_PREFERENCES` object:
+
+```typescript
+export const DEFAULT_USER_PREFERENCES = {
+ // Existing preferences...
+ show_survey_overall: true,
+ show_survey_company: true,
+
+ // Your new preferences
+ new_feature_enabled: false,
+ user_language: 'en',
+ dashboard_layout: 'grid',
+ notification_frequency: 'daily',
+} as const;
+```
+
+### Step 2: Restart Application
+
+That's it! Restart the application and:
+- New users automatically receive the new preferences with default values
+- Existing users can access the new preferences (they'll get defaults when first accessed)
+- No database migration required
+- **API schemas and TypeScript types are automatically updated** from your config changes
+
+### Single Source of Truth
+
+The system automatically generates API validation schemas and TypeScript interfaces from the configuration file. This means:
+
+- **No duplication**: You only define preferences in one place
+- **Completely generic**: Works with any preference type (string, boolean, number)
+- **No special cases**: All preferences are handled uniformly, no hardcoded values
+- **Automatic validation**: API endpoints automatically validate against your config
+- **Type safety**: TypeScript interfaces are auto-generated from your preferences
+- **Zero maintenance**: Adding/removing preferences doesn't require schema updates
+
+## Using the Service Layer
+
+The `UserPreferencesService` provides a clean interface for working with preferences in your application code.
+
+### Basic Operations
+
+```typescript
+import { UserPreferencesService } from '../services/UserPreferencesService';
+import { getDb } from '../db';
+
+const db = getDb();
+const preferencesService = new UserPreferencesService(db);
+
+// Get a specific preference with fallback default
+const theme = await preferencesService.getPreference(userId, 'theme', 'auto');
+
+// Set a single preference
+await preferencesService.setPreference(userId, 'show_survey_overall', false);
+
+// Update multiple preferences at once
+await preferencesService.updatePreferences(userId, {
+ theme: 'dark',
+ sidebar_collapsed: true,
+ email_notifications_enabled: false
+});
+
+// Get all user preferences
+const allPreferences = await preferencesService.getUserPreferences(userId);
+```
+
+### Specialized Methods
+
+```typescript
+// Walkthrough management
+const shouldShow = await preferencesService.shouldShowWalkthrough(userId);
+await preferencesService.completeWalkthrough(userId);
+await preferencesService.cancelWalkthrough(userId);
+
+// Notification acknowledgments
+await preferencesService.acknowledgeNotification(userId, 'welcome-2024');
+```
+
+### Integration in Route Handlers
+
+```typescript
+export default async function myRoute(server: FastifyInstance) {
+ server.get('/my-feature', {
+ preValidation: requirePermission('preferences.view'),
+ }, async (request, reply) => {
+ const userId = request.user!.id;
+ const db = getDb();
+ const preferencesService = new UserPreferencesService(db);
+
+ // Check if user has enabled the feature
+ const featureEnabled = await preferencesService.getPreference(
+ userId,
+ 'new_feature_enabled',
+ false
+ );
+
+ if (!featureEnabled) {
+ return reply.status(403).send({ error: 'Feature not enabled' });
+ }
+
+ // Continue with feature logic...
+ });
+}
+```
+
+## Security Model
+
+The User Preferences System implements a strict self-service security model with important restrictions:
+
+### Core Security Principle: Self-Service Only
+- **Users can ONLY view and modify their own preferences**
+- **Even `global_admin` users CANNOT access other users' preferences**
+- **No admin override capability exists (by design for privacy)**
+- **All preference operations are strictly user-scoped**
+
+### Permissions Required
+- `preferences.view` - Required to read own preferences
+- `preferences.edit` - Required to modify own preferences
+
+### Role Assignments
+- `global_admin` - Has both view and edit permissions (for their own preferences only)
+- `global_user` - Has both view and edit permissions (for their own preferences only)
+- Team roles - No preference permissions (preferences are user-scoped, not team-scoped)
+
+### Access Control Implementation
+- All routes extract `userId` from the authenticated user's session (`request.user!.id`)
+- No route accepts a `userId` parameter - it's always the authenticated user
+- Permission-based authorization happens before validation (security-first pattern)
+- Database queries are automatically scoped to the authenticated user's ID
+
+### What This Means for Developers
+- You cannot build admin tools to manage other users' preferences
+- Support teams cannot directly modify user preferences through the API
+- All preference management is strictly self-service
+- Users have complete privacy and control over their preference data
+
+## Current Available Preferences
+
+The system currently supports these preference categories:
+
+### Survey Preferences
+- `show_survey_overall` (boolean) - Display overall satisfaction survey
+- `show_survey_company` (boolean) - Display company-specific survey
+
+### Walkthrough Preferences
+- `walkthrough_completed` (boolean) - User completed onboarding walkthrough
+- `walkthrough_cancelled` (boolean) - User cancelled onboarding walkthrough
+
+### UI Preferences
+- `theme` (string) - UI theme: 'light', 'dark', or 'auto'
+- `sidebar_collapsed` (boolean) - Sidebar collapsed state
+
+### Notification Preferences
+- `email_notifications_enabled` (boolean) - Email notifications enabled
+- `browser_notifications_enabled` (boolean) - Browser notifications enabled
+- `notification_acknowledgments` (string) - Comma-separated acknowledged notification IDs
+
+### Feature Preferences
+- `beta_features_enabled` (boolean) - Beta features enabled
+
+## Frontend Integration
+
+The User Preferences System integrates seamlessly with frontend applications through the service layer and existing API endpoints. Frontend developers can access preferences through the established API patterns used throughout DeployStack.
+
+## Performance Considerations
+
+The system is optimized for performance with several key features:
+
+### Database Indexing
+- Primary index on `user_id` for fast user lookups
+- Composite index on `user_id, preference_key` for specific preference queries
+- Index on `preference_key` for analytics queries
+- Index on `updated_at` for temporal queries
+
+### Efficient Queries
+- Single query to fetch all user preferences
+- Batch updates for multiple preference changes
+- Automatic type conversion between strings and native types
+
+## Type Safety Features
+
+The system provides comprehensive type safety:
+
+### Runtime Validation
+All preference values are validated against the configuration schema before storage.
+
+### TypeScript Integration
+Full TypeScript support with auto-completion and type checking:
+
+```typescript
+// Type-safe preference access
+const preferences: UserPreferences = await preferencesService.getUserPreferences(userId);
+
+// TypeScript knows the available keys and their types
+const theme: string = preferences.theme || 'auto';
+const sidebarCollapsed: boolean = preferences.sidebar_collapsed || false;
+```
+
+### Configuration-Driven Types
+Types are automatically derived from the configuration, ensuring consistency between defaults and usage.
+
+## Migration Strategy
+
+If you need to rename or remove preferences:
+
+### Renaming Preferences
+1. Add the new preference to config with desired default
+2. Create a migration script to copy old values to new keys
+3. Remove the old preference from config after migration
+4. Restart application
+
+### Removing Preferences
+1. Remove from configuration file
+2. Restart application
+3. Old preference data remains in database but becomes inaccessible
+4. Optionally clean up old data with a maintenance script
+
+## Development Workflow
+
+### Local Development
+1. Add preference to config file
+2. Restart development server
+3. Test with new user registration (gets defaults automatically)
+4. Test with existing users (gets defaults on first access)
+
+### Production Deployment
+1. Deploy code changes with new preferences in config
+2. Restart application servers
+3. New preferences are immediately available
+4. No database downtime or migration required
+
+## Related Documentation
+
+- [API Security](/development/backend/api-security) - Security patterns and authorization
+- [Role Management](/development/backend/roles) - Permission system details
+- [Database Schema](https://github.com/deploystackio/deploystack/blob/main/services/backend/src/db/schema.sqlite.ts) - Complete database schema reference
+
+## Key Benefits
+
+### For Developers
+- **Zero Migration Workflow**: Add preferences without database changes
+- **Type Safety**: Full TypeScript support with runtime validation
+- **Simple API**: Intuitive service layer methods
+- **Performance**: Optimized queries with proper indexing
+
+### For Operations
+- **No Downtime**: Preference additions require no database migrations
+- **Secure**: Permission-based access with proper isolation
+- **Scalable**: Separate table architecture supports complex queries
+- **Maintainable**: Config-driven approach with clear separation of concerns
+
+The User Preferences System represents a modern approach to user settings management, balancing flexibility with performance while maintaining the simplicity that DeployStack developers expect.
diff --git a/docs/development/gateway/oauth.mdx b/docs/development/gateway/oauth.mdx
index 7653e0d..fc807d1 100644
--- a/docs/development/gateway/oauth.mdx
+++ b/docs/development/gateway/oauth.mdx
@@ -35,7 +35,7 @@ The authorization URL includes:
- `response_type=code` for authorization code flow
- `client_id=deploystack-gateway-cli` for client identification
- `redirect_uri=http://localhost:8976/oauth/callback` for callback handling
-- Requested scopes: `mcp:read account:read user:read teams:read offline_access`
+- Requested scopes (see [OAuth Scope Management](#oauth-scope-management) below)
- PKCE parameters: `code_challenge` and `code_challenge_method=S256`
- Random `state` parameter for security
@@ -90,7 +90,7 @@ The gateway is pre-registered with the backend as:
- **Client ID**: `deploystack-gateway-cli`
- **Client Type**: Public client (no secret required)
- **Redirect URIs**: `http://localhost:8976/oauth/callback`, `http://127.0.0.1:8976/oauth/callback`
-- **Allowed Scopes**: `mcp:read`, `account:read`, `user:read`, `teams:read`, `offline_access`
+- **Allowed Scopes**: See source code at `services/gateway/src/utils/auth-config.ts`
- **PKCE**: Required with SHA256 method
- **Token Lifetime**: 1 week access tokens, 30 day refresh tokens
@@ -212,6 +212,50 @@ The OAuth implementation follows security best practices:
For detailed security implementation including credential storage, token expiration, and local file security, see the [Gateway Security Guide](/development/gateway/security).
+## OAuth Scope Management
+
+The gateway requests specific OAuth scopes during authentication to access backend APIs. Scope configuration must stay synchronized between the gateway and backend.
+
+### Current Scopes
+
+For the current list of supported scopes, check the source code at:
+- **Gateway scopes**: `services/gateway/src/utils/auth-config.ts` in the `scopes` array
+- **Backend validation**: `services/backend/src/services/oauth/authorizationService.ts` in the `validateScope()` method
+
+### Adding New Scopes
+
+When the backend adds support for a new OAuth scope, you must update the gateway configuration:
+
+1. **Add the scope** to the `scopes` array in `services/gateway/src/utils/auth-config.ts`
+2. **Add a description** to the `SCOPE_DESCRIPTIONS` object in the same file
+3. **Test the login flow** to ensure the new scope is requested and granted
+
+Example:
+```typescript
+// In services/gateway/src/utils/auth-config.ts
+scopes: [
+ 'mcp:read',
+ 'mcp:categories:read',
+ 'your-new-scope', // Add new scope here
+ // ... other scopes
+],
+
+// And add description
+export const SCOPE_DESCRIPTIONS: Record = {
+ 'mcp:read': 'Access your MCP server installations and configurations',
+ 'your-new-scope': 'Description of what this scope allows', // Add description
+ // ... other descriptions
+};
+```
+
+### Scope Synchronization
+
+**Critical**: The gateway and backend must have matching scope configurations:
+- If backend supports a scope but gateway doesn't request it, users won't get that permission
+- If gateway requests a scope but backend doesn't support it, authentication will fail
+
+Always coordinate scope changes between both services.
+
## Testing OAuth Flow
During development, the OAuth flow can be tested:
diff --git a/docs/meta.json b/docs/meta.json
index 4119dba..d4354a1 100644
--- a/docs/meta.json
+++ b/docs/meta.json
@@ -33,6 +33,6 @@
"development/index",
"development/frontend",
"development/backend",
- "development/gateway"
+ "development/gateway"
]
}
\ No newline at end of file
diff --git a/docs/teams.mdx b/docs/teams.mdx
index 7fbe20a..34d8846 100644
--- a/docs/teams.mdx
+++ b/docs/teams.mdx
@@ -36,7 +36,7 @@ This default team is immediately ready for use - you can start configuring MCP s
### Additional Teams
-You can create additional teams beyond your automatically created default team to organize different projects or environments.
+You can create additional teams beyond your automatically created default team to organize different projects or environments. The team creation limit can be adjusted by administrators in Global Settings (default: 3 teams maximum).
## What Teams Contain
@@ -177,6 +177,7 @@ Team administrators can add new members to their teams (except default teams):
**Limitations**:
- **Default teams**: Cannot add members to your personal default team
- **Existing users only**: Can only add users who already have DeployStack accounts
+- **Member limits**: Non-default teams have a configurable member limit (adjustable in Global Settings, default: 3 members)
### Managing Member Roles