diff --git a/frontend/editor/src/portal/generated/docsManifest.json b/frontend/editor/src/portal/generated/docsManifest.json index 1931a4d9af..dd212cd5e7 100644 --- a/frontend/editor/src/portal/generated/docsManifest.json +++ b/frontend/editor/src/portal/generated/docsManifest.json @@ -19,34 +19,262 @@ "label": "Production Deployment Guide" }, { - "id": "paid-offerings", - "label": "Paid Offerings" + "id": "api", + "label": "API" }, { "id": "modes-and-licensing", "label": "Modes" }, { - "id": "api", - "label": "API" - }, - { - "id": "contribute", - "label": "Contribution guidelines" + "id": "paid-offerings", + "label": "Paid Offerings" }, { "id": "faq", "label": "FAQ" }, + { + "id": "contribute", + "label": "Contribution guidelines" + }, { "id": "analytics-and-telemetry", "label": "Analytics and Telemetry" } ] }, + { + "id": "installation", + "label": "Installation", + "icon": "⤓", + "items": [ + { + "id": "installation/versions", + "label": "Versions" + }, + { + "id": "installation/docker-install", + "label": "Docker Guide" + }, + { + "id": "installation/kubernetes", + "label": "Kubernetes Guide" + }, + { + "id": "installation/mac", + "label": "Mac Installation Guide" + }, + { + "id": "installation/unix", + "label": "Unix Installation Guide" + }, + { + "id": "installation/windows", + "label": "Windows Guide" + }, + { + "id": "installation/development-setup", + "label": "Development Setup Guide" + }, + { + "id": "installation/managed-deployment", + "label": "Managed Desktop Deployment" + }, + { + "id": "installation/path-structure", + "label": "Path Structure" + } + ] + }, + { + "id": "migration", + "label": "Migration from V1 to V2", + "icon": "⇄", + "items": [ + { + "id": "migration/overview", + "label": "Migrating from V1 to V2" + }, + { + "id": "migration/settings-changes", + "label": "Settings Changes from V1 to V2" + }, + { + "id": "migration/new-features", + "label": "New Features in V2" + }, + { + "id": "migration/breaking-changes", + "label": "Breaking Changes in V2" + } + ] + }, + { + "id": "configuration", + "label": "Configuration", + "icon": "⚙", + "items": [ + { + "id": "configuration/configuration", + "label": "Configuration Guide" + } + ] + }, + { + "id": "configuration/security", + "label": "Security & SSO", + "icon": "🛡", + "items": [ + { + "id": "configuration/security/system-and-security", + "label": "Login, System and Security" + }, + { + "id": "configuration/security/single-sign-on-configuration", + "label": "Single Sign-On (SSO) Overview" + }, + { + "id": "configuration/security/oauth-sso-configuration", + "label": "OAuth 2.0 Single Sign-On Configuration" + }, + { + "id": "configuration/security/saml-sso-configuration", + "label": "SAML 2.0 Single Sign-On Configuration" + }, + { + "id": "configuration/security/sign-with-custom-files", + "label": "Visual Sign with Custom File Storage" + }, + { + "id": "configuration/security/ssrf-protection", + "label": "SSRF Protection" + }, + { + "id": "configuration/security/fail2ban", + "label": "Fail2Ban Integration" + }, + { + "id": "configuration/security/audit-logging", + "label": "Audit Logging" + } + ] + }, + { + "id": "configuration/storage", + "label": "Storage & Database", + "icon": "◇", + "items": [ + { + "id": "configuration/storage/database", + "label": "Database Backups" + }, + { + "id": "configuration/storage/external-database", + "label": "External Database" + }, + { + "id": "configuration/storage/file-sharing-and-storage", + "label": "File Sharing and Storage" + }, + { + "id": "configuration/storage/folderscanning", + "label": "Folder Scanning" + }, + { + "id": "configuration/storage/google-drive-file-picker", + "label": "Google Drive File Picker" + } + ] + }, + { + "id": "configuration/automation", + "label": "Automation & Integrations", + "icon": "◇", + "items": [ + { + "id": "configuration/automation/pipeline", + "label": "Pipeline Automation (Automate)" + }, + { + "id": "configuration/automation/mcp-server", + "label": "MCP Server" + }, + { + "id": "configuration/automation/telegram-bot", + "label": "Telegram Bot Integration" + }, + { + "id": "configuration/automation/usage-monitoring", + "label": "Usage Monitoring" + } + ] + }, + { + "id": "configuration/customisation", + "label": "Customisation", + "icon": "◇", + "items": [ + { + "id": "configuration/customisation/ui-customisation", + "label": "UI Customisation" + }, + { + "id": "configuration/customisation/endpoint-or-feature-customisation", + "label": "Endpoints Customisation" + }, + { + "id": "configuration/customisation/extra-settings", + "label": "Custom Settings Configuration" + }, + { + "id": "configuration/customisation/other-customisations", + "label": "Other Customisations" + }, + { + "id": "configuration/customisation/keyboard-shortcuts", + "label": "Keyboard Shortcuts" + }, + { + "id": "configuration/customisation/mobile-scanner", + "label": "Mobile Scanner Configuration" + }, + { + "id": "configuration/customisation/pdf-to-cbr-conversion", + "label": "Enabling PDF to CBR Conversion in Stirling PDF" + } + ] + }, + { + "id": "configuration/operations", + "label": "Performance & Operations", + "icon": "◇", + "items": [ + { + "id": "configuration/operations/ocr", + "label": "OCR (Optical Character Recognition)" + }, + { + "id": "configuration/operations/performance-optimization", + "label": "Performance Optimization & Sizing" + }, + { + "id": "configuration/operations/process-limits", + "label": "Process Limits" + }, + { + "id": "configuration/operations/libreoffice-parallel-processing", + "label": "LibreOffice Parallel Processing" + }, + { + "id": "configuration/operations/diagnostics", + "label": "Diagnostics & Reporting Issues" + } + ] + }, { "id": "functionality", - "label": "Functionality", + "label": "Tools", "icon": "▤", "items": [ { @@ -158,217 +386,6 @@ "label": "Content & Editing" } ] - }, - { - "id": "installation", - "label": "Installation", - "icon": "⤓", - "items": [ - { - "id": "installation/versions", - "label": "Versions" - }, - { - "id": "installation/docker-install", - "label": "Docker Guide" - }, - { - "id": "installation/kubernetes", - "label": "Kubernetes Guide" - }, - { - "id": "installation/mac", - "label": "Mac Installation Guide" - }, - { - "id": "installation/unix", - "label": "Unix Installation Guide" - }, - { - "id": "installation/windows", - "label": "Windows Guide" - }, - { - "id": "installation/development-setup", - "label": "Development Setup Guide" - }, - { - "id": "installation/managed-deployment", - "label": "Managed Desktop Deployment" - }, - { - "id": "installation/path-structure", - "label": "Path Structure" - } - ] - }, - { - "id": "migration", - "label": "Migration from V1 to V2", - "icon": "⇄", - "items": [ - { - "id": "migration/overview", - "label": "Migrating from V1 to V2" - }, - { - "id": "migration/settings-changes", - "label": "Settings Changes from V1 to V2" - }, - { - "id": "migration/new-features", - "label": "New Features in V2" - }, - { - "id": "migration/breaking-changes", - "label": "Breaking Changes in V2" - } - ] - }, - { - "id": "configuration", - "label": "Configuration", - "icon": "⚙", - "items": [ - { - "id": "configuration/configuration", - "label": "Configuration Guide" - }, - { - "id": "configuration/system-and-security", - "label": "Login, System and Security" - }, - { - "id": "configuration/oauth-sso-configuration", - "label": "OAuth 2.0 Single Sign-On Configuration" - }, - { - "id": "configuration/single-sign-on-configuration", - "label": "Single Sign-On (SSO) Overview" - }, - { - "id": "configuration/ui-customisation", - "label": "UI Customisation" - }, - { - "id": "configuration/google-drive-file-picker", - "label": "Google Drive File Picker" - }, - { - "id": "configuration/usage-monitoring", - "label": "Usage Monitoring" - }, - { - "id": "configuration/endpoint-or-feature-customisation", - "label": "Endpoints Customisation" - }, - { - "id": "configuration/folderscanning", - "label": "Folder Scanning" - }, - { - "id": "configuration/ocr", - "label": "OCR (Optical Character Recognition)" - }, - { - "id": "configuration/fail2ban", - "label": "Fail2Ban Integration" - }, - { - "id": "configuration/external-database", - "label": "External Database" - }, - { - "id": "configuration/database", - "label": "Database Backups" - }, - { - "id": "configuration/pipeline", - "label": "Pipeline Automation (Automate)" - }, - { - "id": "configuration/extra-settings", - "label": "Custom Settings Configuration" - }, - { - "id": "configuration/sign-with-custom-files", - "label": "Visual Sign with Custom File Storage" - }, - { - "id": "configuration/file-sharing-and-storage", - "label": "File Sharing and Storage" - }, - { - "id": "configuration/other-customisations", - "label": "Other Customisations" - }, - { - "id": "configuration/telegram-bot", - "label": "Telegram Bot Integration" - }, - { - "id": "configuration/ssrf-protection", - "label": "SSRF Protection" - }, - { - "id": "configuration/process-limits", - "label": "Process Limits" - }, - { - "id": "configuration/audit-logging", - "label": "Audit Logging" - }, - { - "id": "configuration/keyboard-shortcuts", - "label": "Keyboard Shortcuts" - }, - { - "id": "configuration/mobile-scanner", - "label": "Mobile Scanner Configuration" - }, - { - "id": "configuration/diagnostics", - "label": "Diagnostics & Reporting Issues" - }, - { - "id": "configuration/libreoffice-parallel-processing", - "label": "LibreOffice Parallel Processing" - }, - { - "id": "configuration/performance-optimization", - "label": "Performance Optimization & Sizing" - } - ] - }, - { - "id": "configuration/saml-sso-configuration", - "label": "SAML SSO Configuration", - "icon": "⚙", - "items": [ - { - "id": "configuration/saml-sso-configuration/saml-sso-configuration", - "label": "SAML 2.0 Single Sign-On Configuration" - } - ] - }, - { - "id": "advanced-configuration", - "label": "Advanced Configuration", - "icon": "⚙", - "items": [ - { - "id": "advanced-configuration/other-customisations", - "label": "Other Customisations" - }, - { - "id": "advanced-configuration/mcp-server", - "label": "MCP Server" - }, - { - "id": "advanced-configuration/pdf-to-cbr-conversion", - "label": "Enabling PDF to CBR Conversion in Stirling PDF" - } - ] } ], "docs": { @@ -377,274 +394,266 @@ "title": "API", "description": "Overview of API offering in S-PDF", "section": "overview", - "markdown": "## Stirling PDF API\n\nStirling PDF exposes a simple API for easy integration with external scripts. You can access the API documentation in two ways:\n\n1. Local Swagger UI at `/swagger-ui.html` on your Stirling PDF instance\n2. Online [Swagger Documentation](https://app.swaggerhub.com/apis-docs/Frooodle/Stirling-PDF/)\n\nYou can also access the documentation through the settings menu (gear icon in the top-right corner).\n\n## Accessing API Documentation\n\n### Local Swagger UI\nYour Stirling PDF instance includes built-in API documentation:\n1. Navigate to `http://your-instance:port/swagger-ui.html`\n2. Or append `/swagger-ui.html` to your Stirling PDF URL\n3. This provides an interactive documentation interface where you can:\n - View all available endpoints\n - Test API calls directly\n - See request/response schemas\n - View authentication requirements\n\n### Settings Menu Access\n1. Click the gear icon (⚙️) in the top-right corner\n2. Look for the \"API Documentation\" or \"API\" link\n3. This will take you to the local Swagger UI\n\n## API Authentication\n\nWhen security is enabled, all API requests require authentication. There are two ways to handle API authentication:\n\n### User-Specific API Keys\n1. Obtain your API key:\n - Log into Stirling PDF\n - Go to Account Settings (via the gear icon)\n - Find your API key in the account details\n\n### Global API Key\nYou can set a custom global API key using the environment variable:\n```bash\nSECURITY_CUSTOMGLOBALAPIKEY=your-custom-api-key\n```\nThis allows you to set a single API key that works regardless of user authentication.\n\n2. Include the API key in all requests:\n ```http\n X-API-KEY: your-api-key-here\n ```\n\n3. Example authenticated request:\n ```bash\n curl -X POST \"http://localhost:8080/api/v1/security/add-watermark\" \\\n -H \"X-API-KEY: your-api-key-here\" \\\n -H \"Content-Type: multipart/form-data\" \\\n ...\n ```\n\n## Endpoint Paths\n\n> **ℹ️ Info**\n>\n> Every operation lives under `/api/v1//`, where the category is one of `security`, `general`, `misc`, `convert`, etc. For example, \"Add Watermark\" is at `/api/v1/security/add-watermark`. The exact path for any operation is shown in the [Swagger UI](#local-swagger-ui).\n\n\n### AI assistants / MCP\n\nTo drive these endpoints from an AI assistant (Claude Desktop, Cursor, etc.) over the Model Context Protocol, see [MCP Server](doc:advanced-configuration/mcp-server).\n\n## API Limitations\n\nStirling PDF's feature set is not entirely confined to the backend, hence not all functionalities are accessible via the API. Certain operations, such as the \"view-pdf\" or \"visually sign\", are executed exclusively on the front-end, and as such, they are only available through the Web-UI. If you encounter a situation where some API endpoints appear to be absent, it is likely attributable to these front-end exclusive features.\n\nStirling PDF also has statistic and health endpoints to integrate with monitoring/dashboard applications.\n\n## Example CURL Commands\n\n\n \n ```bash\n curl -X POST \"http://localhost:8080/api/v1/security/add-watermark\" \\\n -H \"Content-Type: multipart/form-data\" \\\n -F \"fileInput=@/Users/username/Downloads/sample-1_cropped.pdf\" \\\n -F \"watermarkType=text\" \\\n -F \"watermarkText=YOUR_WATERMARK_TEXT\" \\\n -F \"alphabet=roman\" \\\n -F \"fontSize=30\" \\\n -F \"rotation=0\" \\\n -F \"opacity=0.5\" \\\n -F \"widthSpacer=50\" \\\n -F \"heightSpacer=50\" \\\n > \"/Users/username/Downloads/output.pdf\"\n ```\n \n \n ```bash\n curl -X POST \"http://localhost:8080/api/v1/security/add-watermark\" ^\n -H \"Content-Type: multipart/form-data\" ^\n -F \"fileInput=@C:\\Users\\systo\\Downloads\\sample-1_cropped.pdf\" ^\n -F \"watermarkType=text\" ^\n -F \"watermarkText=YOUR_WATERMARK_TEXT\" ^\n -F \"alphabet=roman\" ^\n -F \"fontSize=30\" ^\n -F \"rotation=0\" ^\n -F \"opacity=0.5\" ^\n -F \"widthSpacer=50\" ^\n -F \"heightSpacer=50\" ^\n > \"C:\\Users\\systo\\Downloads\\output.pdf\"\n ```\n \n\n\n## Integrations (n8n, Zapier, Make, Power Automate, etc.)\n\nStirling PDF does not ship dedicated plugins or nodes for any specific automation platform. Integration is **via the REST API** documented above, which works with any tool that can make an authenticated HTTP request.\n\n### What this looks like in practice\n\n- **n8n**: use the built-in [HTTP Request node](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/) pointed at your Stirling PDF instance. Set method `POST`, content type `multipart/form-data`, attach the binary as `fileInput`, add the `X-API-KEY` header, and connect the binary output to a `Read/Write Binary File` node or onward to your storage.\n- **Zapier / Make / Power Automate**: use the generic HTTP / Webhooks action with the same multipart pattern.\n- **Home Assistant**: a `rest_command` definition pointing at the appropriate Stirling endpoint.\n- **Bash / Python / JavaScript**: any HTTP client (`curl`, `requests`, `fetch`) - the curl examples on this page translate directly.\n\n### Common automation recipe: chain multiple operations in one call\n\nRather than wiring 5 separate HTTP nodes for \"OCR then compress then watermark then sign...\", you could use the **pipeline endpoint** to chain everything in one request:\n\n- Endpoint: `POST /api/v1/pipeline/handleData`\n- Request: multipart with one or more `fileInput` parts plus a `json` field containing the full pipeline configuration\n- Response: a single processed file, or a ZIP if the pipeline produced multiple outputs\n\nFull schema, operation list, parameter reference, and curl examples: see **[Pipeline Automation](doc:configuration/pipeline)**.\n\n### Building the pipeline JSON\n\nThe fastest path is:\n1. Build the workflow visually in the **Automate** tool inside Stirling PDF.\n2. Click **Export for Folder Scanning** in the save panel - this produces the JSON in the format the API expects.\n3. Drop that JSON into your automation tool's HTTP request as the `json` form field.\n\nRe-import the same file later via the Automate UI's import dialog to round-trip workflows between machines.\n\n### Authentication for automation tools\n\nWhen security is enabled, set a global API key once via `SECURITY_CUSTOMGLOBALAPIKEY=` and reference it from your automation tool as the `X-API-KEY` header. This avoids needing per-user logins from headless scripts.", + "markdown": "## Stirling PDF API\n\nStirling PDF exposes a simple API for easy integration with external scripts. You can access the API documentation in two ways:\n\n1. Local Swagger UI at `/swagger-ui.html` on your Stirling PDF instance\n2. Online [Swagger Documentation](https://app.swaggerhub.com/apis-docs/Frooodle/Stirling-PDF/)\n\nYou can also access the documentation through the settings menu (gear icon in the top-right corner).\n\n## Accessing API Documentation\n\n### Local Swagger UI\nYour Stirling PDF instance includes built-in API documentation:\n1. Navigate to `http://your-instance:port/swagger-ui.html`\n2. Or append `/swagger-ui.html` to your Stirling PDF URL\n3. This provides an interactive documentation interface where you can:\n - View all available endpoints\n - Test API calls directly\n - See request/response schemas\n - View authentication requirements\n\n### Settings Menu Access\n1. Click the gear icon (⚙️) in the top-right corner\n2. Look for the \"API Documentation\" or \"API\" link\n3. This will take you to the local Swagger UI\n\n## API Authentication\n\nWhen security is enabled, all API requests require authentication. There are two ways to handle API authentication:\n\n### User-Specific API Keys\n1. Obtain your API key:\n - Log into Stirling PDF\n - Go to Account Settings (via the gear icon)\n - Find your API key in the account details\n\n### Global API Key\nYou can set a custom global API key using the environment variable:\n```bash\nSECURITY_CUSTOMGLOBALAPIKEY=your-custom-api-key\n```\nThis allows you to set a single API key that works regardless of user authentication.\n\n2. Include the API key in all requests:\n ```http\n X-API-KEY: your-api-key-here\n ```\n\n3. Example authenticated request:\n ```bash\n curl -X POST \"http://localhost:8080/api/v1/security/add-watermark\" \\\n -H \"X-API-KEY: your-api-key-here\" \\\n -H \"Content-Type: multipart/form-data\" \\\n ...\n ```\n\n## Endpoint Paths\n\n> **ℹ️ Info**\n>\n> Every operation lives under `/api/v1//`, where the category is one of `security`, `general`, `misc`, `convert`, etc. For example, \"Add Watermark\" is at `/api/v1/security/add-watermark`. The exact path for any operation is shown in the [Swagger UI](#local-swagger-ui).\n\n\n### AI assistants / MCP\n\nTo drive these endpoints from an AI assistant (Claude Desktop, Cursor, etc.) over the Model Context Protocol, see [MCP Server](doc:configuration/automation/mcp-server).\n\n## API Limitations\n\nStirling PDF's feature set is not entirely confined to the backend, hence not all functionalities are accessible via the API. Certain operations, such as the \"view-pdf\" or \"visually sign\", are executed exclusively on the front-end, and as such, they are only available through the Web-UI. If you encounter a situation where some API endpoints appear to be absent, it is likely attributable to these front-end exclusive features.\n\nStirling PDF also has statistic and health endpoints to integrate with monitoring/dashboard applications.\n\n## Example CURL Commands\n\n\n \n ```bash\n curl -X POST \"http://localhost:8080/api/v1/security/add-watermark\" \\\n -H \"Content-Type: multipart/form-data\" \\\n -F \"fileInput=@/Users/username/Downloads/sample-1_cropped.pdf\" \\\n -F \"watermarkType=text\" \\\n -F \"watermarkText=YOUR_WATERMARK_TEXT\" \\\n -F \"alphabet=roman\" \\\n -F \"fontSize=30\" \\\n -F \"rotation=0\" \\\n -F \"opacity=0.5\" \\\n -F \"widthSpacer=50\" \\\n -F \"heightSpacer=50\" \\\n > \"/Users/username/Downloads/output.pdf\"\n ```\n \n \n ```bash\n curl -X POST \"http://localhost:8080/api/v1/security/add-watermark\" ^\n -H \"Content-Type: multipart/form-data\" ^\n -F \"fileInput=@C:\\Users\\systo\\Downloads\\sample-1_cropped.pdf\" ^\n -F \"watermarkType=text\" ^\n -F \"watermarkText=YOUR_WATERMARK_TEXT\" ^\n -F \"alphabet=roman\" ^\n -F \"fontSize=30\" ^\n -F \"rotation=0\" ^\n -F \"opacity=0.5\" ^\n -F \"widthSpacer=50\" ^\n -F \"heightSpacer=50\" ^\n > \"C:\\Users\\systo\\Downloads\\output.pdf\"\n ```\n \n\n\n## Integrations (n8n, Zapier, Make, Power Automate, etc.)\n\nStirling PDF does not ship dedicated plugins or nodes for any specific automation platform. Integration is **via the REST API** documented above, which works with any tool that can make an authenticated HTTP request.\n\n### What this looks like in practice\n\n- **n8n**: use the built-in [HTTP Request node](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/) pointed at your Stirling PDF instance. Set method `POST`, content type `multipart/form-data`, attach the binary as `fileInput`, add the `X-API-KEY` header, and connect the binary output to a `Read/Write Binary File` node or onward to your storage.\n- **Zapier / Make / Power Automate**: use the generic HTTP / Webhooks action with the same multipart pattern.\n- **Home Assistant**: a `rest_command` definition pointing at the appropriate Stirling endpoint.\n- **Bash / Python / JavaScript**: any HTTP client (`curl`, `requests`, `fetch`) - the curl examples on this page translate directly.\n\n### Common automation recipe: chain multiple operations in one call\n\nRather than wiring 5 separate HTTP nodes for \"OCR then compress then watermark then sign...\", you could use the **pipeline endpoint** to chain everything in one request:\n\n- Endpoint: `POST /api/v1/pipeline/handleData`\n- Request: multipart with one or more `fileInput` parts plus a `json` field containing the full pipeline configuration\n- Response: a single processed file, or a ZIP if the pipeline produced multiple outputs\n\nFull schema, operation list, parameter reference, and curl examples: see **[Pipeline Automation](doc:configuration/automation/pipeline)**.\n\n### Building the pipeline JSON\n\nThe fastest path is:\n1. Build the workflow visually in the **Automate** tool inside Stirling PDF.\n2. Click **Export for Folder Scanning** in the save panel - this produces the JSON in the format the API expects.\n3. Drop that JSON into your automation tool's HTTP request as the `json` form field.\n\nRe-import the same file later via the Automate UI's import dialog to round-trip workflows between machines.\n\n### Authentication for automation tools\n\nWhen security is enabled, set a global API key once via `SECURITY_CUSTOMGLOBALAPIKEY=` and reference it from your automation tool as the `X-API-KEY` header. This avoids needing per-user logins from headless scripts.", "sourcePath": "docs/API.md", "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/API.md" }, - "advanced-configuration/mcp-server": { - "id": "advanced-configuration/mcp-server", - "title": "MCP Server", - "description": "Expose Stirling PDF's tools to MCP clients over a built-in Model Context Protocol server", - "section": "advanced-configuration", - "markdown": "Stirling PDF ships a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server. MCP is the open standard MCP clients (Claude Desktop, the MCP Inspector, IDE agents, and custom tools) use to discover and call tools on a remote server. When enabled, Stirling PDF exposes its PDF operations as MCP tools so an MCP-capable assistant can run them on your behalf.\n\nThe MCP server is built into the Stirling PDF self-hosted server and the desktop app in Local / Self-hosted modes. It is **off by default** and must be enabled and configured per deployment.\n\n> **ℹ️ Info: Self-hosted capability**\n>\n> This page documents the MCP server you run on your own Stirling PDF instance. The per-user MCP tab in Stirling Cloud is a separate, cloud-only surface and is not covered here. For where each deployment mode applies, see [Modes](doc:modes-and-licensing).\n\n\n---\n\n## Enable the server\n\nThe MCP server runs only when `mcp.enabled` is `true`. While off, there is no `/mcp` endpoint and no MCP metadata.\n\nTurn it on either way:\n\n- **Settings file / environment variable**: set `mcp.enabled: true` in `settings.yml`, or the environment variable `MCP_ENABLED=true`. A restart applies file edits.\n- **Admin UI**: open **Admin Settings → MCP Server**. This page is shown to admins, and on instances where login is disabled. Saving prompts for a restart.\n\nEnabling alone is not enough - you also need to choose and configure an [authentication mode](#authentication) before clients can call tools.\n\n---\n\n## Transport and protocol\n\n- **Endpoint**: `POST /mcp` on the same host and port as the rest of Stirling PDF.\n- **Protocol**: JSON-RPC 2.0 over streamable-HTTP.\n- **Supported MCP protocol versions**: `2025-06-18` (preferred), `2025-03-26`, and `2024-11-05`. The server echoes the client's requested version when it is supported, otherwise it advertises the preferred version.\n\n---\n\n## Tools exposed\n\nThe server presents a small set of category tools rather than one tool per operation. An MCP client lists them, then drills into a specific PDF operation using `stirling_describe_operation`.\n\n| Tool | Purpose |\n|---|---|\n| `stirling_describe_operation` | Look up the parameters and JSON schema for a specific operation id. |\n| `stirling_pages` | Page-level operations (merge, split, rotate, reorder, add blank pages, and similar). |\n| `stirling_convert` | Conversions to and from PDF (images, office formats, and similar). |\n| `stirling_misc` | Miscellaneous utilities (compress, flatten, repair, and similar). |\n| `stirling_security` | Security operations (encrypt, decrypt, permissions, and similar). |\n| `stirling_upload` | Store a file server-side and get back a `fileId` for large inputs. |\n| `stirling_download` | Fetch a result that was returned by reference rather than inline. |\n| `stirling_ai` | AI-engine capabilities. **Not usable in self-hosted - see the caveat below.** |\n\n> **⚠️ Warning: `stirling_ai` requires a Stirling Cloud AI engine**\n>\n> `stirling_ai` only has capabilities when a Stirling AI engine is configured. The AI engine is a Stirling Cloud feature and is **not available in self-hosted** today, so on a self-hosted server `stirling_ai` exposes nothing and only the PDF tools above are usable over MCP.\n\n\n---\n\n## Authentication\n\nPick one of two modes with `mcp.auth.mode`.\n\n### OAuth2 resource server (`oauth`, default)\n\nIn OAuth mode the `/mcp` endpoint runs as an OAuth2 resource server: it validates incoming JWTs (signature, issuer, expiry, and audience) and binds each token to an existing Stirling account. It publishes RFC 9728 protected-resource metadata at `/.well-known/oauth-protected-resource` so MCP clients can discover the authorization server.\n\n| Key | Env | Default | Purpose |\n|---|---|---|---|\n| `mcp.auth.issuerUri` | `MCP_AUTH_ISSUERURI` | empty | OAuth2 issuer URI (e.g. `http://localhost:9000`). **Required** in OAuth mode; every token is rejected until it is set. |\n| `mcp.auth.jwksUri` | `MCP_AUTH_JWKSURI` | empty | JWKS URI. Blank means it is derived from the issuer's `/.well-known/openid-configuration`. |\n| `mcp.auth.resourceId` | `MCP_AUTH_RESOURCEID` | empty | RFC 8707 resource identifier of this server. Must equal the public `/mcp` URL clients call and **end in `/mcp`** (e.g. `http://localhost:8080/mcp`). Tokens that do not list it in `aud` are rejected. |\n| `mcp.auth.acceptedAudiences` | `MCP_AUTH_ACCEPTEDAUDIENCES` | `[]` | Extra `aud` values accepted on top of `resourceId`. Empty keeps strict RFC 8707 binding. Use this for IdPs that cannot mint a resource-specific audience (e.g. Supabase always issues `aud=authenticated`). |\n| `mcp.auth.usernameClaim` | `MCP_AUTH_USERNAMECLAIM` | `sub` | JWT claim matched against a Stirling username. Set to `email` or `preferred_username` if your IdP maps users differently. |\n| `mcp.auth.requireExistingAccount` | `MCP_AUTH_REQUIREEXISTINGACCOUNT` | `true` | Reject tokens whose subject has no enabled Stirling account. Keep `true` unless you intend open access for any IdP-valid token. |\n| `mcp.scopesEnabled` | `MCP_SCOPESENABLED` | `true` | Enforce the `mcp.tools.read` / `mcp.tools.write` scopes. Read-style tools require `mcp.tools.read`; mutating operations require `mcp.tools.write`. Set `false` only if your IdP can issue a single coarse token. |\n\nEach validated token is bound to the matching Stirling account so that audit and attribution are correct.\n\n### API key (`apikey`)\n\nAPI-key mode is the low-friction option for self-hosters with no external identity provider. Set `mcp.auth.mode: apikey` (env `MCP_AUTH_MODE=apikey`) and clients authenticate with an existing per-user Stirling API key.\n\nSend the key as either header:\n\n```text\nX-API-KEY: \n```\n\nor\n\n```text\nAuthorization: Bearer \n```\n\nThe key must belong to an existing, enabled account (generate one under **Account → API Keys** - see [API documentation](doc:api)). No external IdP, OAuth, or JWKS configuration is needed.\n\n---\n\n## Restrict which operations are exposed\n\nTwo MCP-level lists control which operations clients can see and call. They use the same kebab-case operation ids as the [Endpoint or Feature Customisation](doc:configuration/endpoint-or-feature-customisation) page (e.g. `compress-pdf`).\n\n| Key | Env | Default | Behaviour |\n|---|---|---|---|\n| `mcp.allowedOperations` | `MCP_ALLOWEDOPERATIONS` | `[]` | When **non-empty**, acts as a strict allow-list - only these operations are exposed over MCP; everything else is hidden, undescribable, and uninvocable. Empty means allow all. |\n| `mcp.blockedOperations` | `MCP_BLOCKEDOPERATIONS` | `[]` | A deny-list. Anything listed is always removed, applied **after** the allow-list, so a blocked id wins even if it is also allowed. |\n\nThese lists layer **on top of** the global [`endpoints.toRemove` / `endpoints.groupsToRemove`](doc:configuration/endpoint-or-feature-customisation) configuration. An operation disabled globally is never exposed over MCP regardless of these lists.\n\n---\n\n## Limits\n\n| Key | Env | Default | Purpose |\n|---|---|---|---|\n| `mcp.maxRequestBytes` | `MCP_MAXREQUESTBYTES` | 10 MB | Maximum MCP request body size. Inline file uploads ride in the JSON-RPC body, so this caps how large an inline input can be. |\n| `mcp.maxInlineResponseBytes` | `MCP_MAXINLINERESPONSEBYTES` | 10 MB | Results up to this size return inline as base64; larger results return a `fileId` instead, which the client fetches with `stirling_download`. |\n| `mcp.engineCapabilityRefreshMinutes` | `MCP_ENGINECAPABILITYREFRESHMINUTES` | `5` | How often the AI capabilities manifest is refreshed from the engine (only relevant when an AI engine is available). |\n\n---\n\n## Troubleshooting and operability\n\n- **Startup config validation**: when MCP is enabled, the server validates the resolved config at boot and logs findings. Misconfiguration (missing issuer, a `resourceId` that does not end in `/mcp`, a `username-claim` of `sub` with `require-existing-account=true`, and similar) is logged as a warning so it surfaces in the logs instead of as a later rejected-token `401`.\n- **Meaningful `401` responses**: a rejected OAuth token returns a `WWW-Authenticate` header carrying a real `error_description` (audience, issuer, or expiry mismatch), plus a `resource_metadata` pointer for discovery. A tokenless `401` is the normal discovery handshake, not an error.\n- **Audit logging**: MCP calls are attributed to the bound Stirling account, and no secret is written to the audit log.\n\n---\n\n## Connect a client\n\nPoint any MCP client at `http://your-host:8080/mcp` (use your real host, port, and scheme).\n\n**MCP Inspector** (quick manual testing):\n\n```bash\nnpx @modelcontextprotocol/inspector\n```\n\nThen set the transport to streamable-HTTP and the URL to your `/mcp` endpoint, adding the appropriate auth header (`X-API-KEY` in API-key mode, or a Bearer token in OAuth mode).\n\n**Claude Desktop** via the `mcp-remote` bridge - add to your Claude Desktop config:\n\n```json\n{\n \"mcpServers\": {\n \"stirling-pdf\": {\n \"command\": \"npx\",\n \"args\": [\n \"-y\",\n \"mcp-remote\",\n \"http://your-host:8080/mcp\",\n \"--header\",\n \"X-API-KEY:your-stirling-api-key\"\n ]\n }\n }\n}\n```\n\nIn OAuth mode, drop the `X-API-KEY` header and let `mcp-remote` complete the OAuth flow against your configured issuer.\n\n---\n\n## Related Documentation\n\n- **[API documentation](doc:api)** - generate the per-user API key used in API-key mode\n- **[Endpoint or Feature Customisation](doc:configuration/endpoint-or-feature-customisation)** - the operation ids and global enable/disable config the MCP lists build on\n- **[Modes](doc:modes-and-licensing)** - where each deployment mode and feature applies", - "sourcePath": "docs/Advanced Configuration/MCP-Server.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Advanced Configuration/MCP-Server.md" - }, - "advanced-configuration/other-customisations": { - "id": "advanced-configuration/other-customisations", - "title": "Other Customisations", - "section": "advanced-configuration", - "markdown": "Stirling PDF offers various other customisation options, such as:\n\n### Defaulting Language\nDefault language selection via the `SYSTEM_DEFAULTLOCALE` environment variable. Accepted values include `de-DE`, `fr-FR`, `ar-AR` and all other languages codes that are within Stirling PDFs current list.\n\n### Google Search Visibility (robots.txt)\nEnable or disable search engine visibility with the `ALLOW_GOOGLE_VISIBILITY` variable.\n\n### Custom Root path\nRedirect the root path of the application using `APP_ROOT_PATH`.\nThis is for changing websites like stirlingtools.com to instead host the interface at stirlingtools.com/`APP_ROOT_PATH` like stirlingtools.com/demo\n\n### Enable/Disable Analytics\nAnalytics can be enabled/disabled with ``SYSTEM_ENABLEANALYTICS`` or\n```yaml\nsystem:\n enableAnalytics: 'true'\n```\nIn configs/Settings.yml\n\n### Using an outgoing HTTP(S) proxy\nTo make Stirling PDF use an outgoing proxy server (e.g. for checking the license validity):\n\n\n \n ```bash\n JAVA_CUSTOM_OPTS=\"-Dhttp.proxyHost=proxyserver -Dhttp.proxyPort=8888 -Dhttp.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local' -Dhttps.proxyHost=proxyserver -Dhttps.proxyPort=8888 -Dhttps.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local'\"\n ```\n \n \n ```bash\n docker run -d \\\n -p 8080:8080 \\\n -e JAVA_CUSTOM_OPTS=\"-Dhttp.proxyHost=proxyserver -Dhttp.proxyPort=8888 -Dhttp.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local' -Dhttps.proxyHost=proxyserver -Dhttps.proxyPort=8888 -Dhttps.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local'\" \\\n stirlingtools/stirling-pdf:latest\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n image: stirlingtools/stirling-pdf:latest\n environment:\n JAVA_CUSTOM_OPTS: \"-Dhttp.proxyHost=proxyserver -Dhttp.proxyPort=8888 -Dhttp.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local' -Dhttps.proxyHost=proxyserver -Dhttps.proxyPort=8888 -Dhttps.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local'\"\n ```", - "sourcePath": "docs/Advanced Configuration/Other Customisations.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Advanced Configuration/Other Customisations.md" - }, - "advanced-configuration/pdf-to-cbr-conversion": { - "id": "advanced-configuration/pdf-to-cbr-conversion", - "title": "Enabling PDF to CBR Conversion in Stirling PDF", - "section": "advanced-configuration", - "markdown": "## Overview\n\nStirling PDF can convert PDF files into the Comic Book RAR (`.cbr`) format. This process relies on an external command-line utility, `rar`, which is not included by default. To enable this feature, you must first install the `rar` utility on your system and then make it accessible to Stirling PDF.\n\n### What is a CBR file?\n\nA CBR (Comic Book RAR) file is an archive used for distributing digital comic books. It is essentially a collection of sequential image files (e.g., JPEG, PNG) compressed into a single file using RAR compression.\n\nWhile CBR is a popular format, it requires the proprietary `rar` utility for creation. Its more common, open-standard alternative is CBZ (Comic Book ZIP), which is supported by Stirling PDF out of the box.\n\n-----\n\n## Step 1: Install the `rar` Command-Line Utility\n\nThis is a mandatory prerequisite for both Docker and non-Docker setups. The `rar` executable must be installed on the host machine.\n\n### Linux\n\nThe easiest method is to use your distribution's package manager.\n\n**Debian / Ubuntu:**\nThe `rar` package is available in the `non-free` repository.\n\n```bash\nsudo apt update\nsudo apt install rar\n```\n\n**Fedora / CentOS / RHEL:**\nThe `rar` package is available in the RPM Fusion \"non-free\" repository.\n\n```bash\n# First, enable the RPM Fusion non-free repository for your system.\n# See https://rpmfusion.org/Configuration for instructions.\n\n# Then, install rar\nsudo dnf install rar # For Fedora, RHEL 8+, CentOS Stream\n# or\nsudo yum install rar # For CentOS 7\n```\n\n**Manual Installation (Any Linux Distribution):**\n\n1. Visit the official download page: [rarlab.com/download.htm](https://www.rarlab.com/download.htm).\n2. Download the \"RAR for Linux x64\" command-line version.\n3. Extract the archive and install the binary:\n ```bash\n # The version number (e.g., 712, as of writing this guide) will change.\n # Use the actual filename.\n tar -xzf rarlinux-x64-*.tar.gz\n\n # Move the binary to a standard location in your system's PATH\n sudo mv rar/rar /usr/local/bin/\n\n # Ensure it has execute permissions\n sudo chmod +x /usr/local/bin/rar\n ```\n\n### Windows\n\n1. Download the \"WinRAR and RAR command line tools\" from [rarlab.com/download.htm](https://www.rarlab.com/download.htm).\n2. Extract the downloaded archive.\n3. Copy the `rar.exe` file to a folder that is included in your system's `PATH` environment variable. A common and reliable location is `C:\\Windows\\System32`.\n4. If Stirling PDF is already running, restart it to ensure it recognizes the updated `PATH`.\n\n### macOS\n\nThe recommended method is to use the [Homebrew](https://brew.sh/) package manager.\n\n```bash\nbrew install rar\n```\n\n-----\n\n## Step 2: Configure Stirling PDF\n\nAfter installing `rar` on your host system, follow the appropriate instructions for your environment.\n\n### For Non-Docker Users\n\nIf you installed Stirling PDF directly on your operating system (without Docker), no further configuration is needed. As long as the `rar` command is available in your system's `PATH`, Stirling PDF will automatically (after restart) detect and use it.\n\n### For Docker Users\n\nFor the binary to be accessible inside the container, you have to mount the binary as a volume.\n\nUpdate your `docker-compose.yml` to include the volume mount. The path on the host side must match where you installed `rar`.\n\n```yaml\nservices:\n stirling-pdf:\n image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n ports:\n - '8080:8080'\n volumes:\n - ./StirlingPDF/trainingData:/usr/share/tessdata\n - ./StirlingPDF/extraConfigs:/configs\n - ./StirlingPDF/customFiles:/customFiles/\n - ./StirlingPDF/logs:/logs/\n - ./StirlingPDF/pipeline:/pipeline/\n # Add the following line to mount the rar binary\n - /usr/local/bin/rar:/usr/local/bin/rar:ro\n```\n\n**Note for Windows Docker Users:**\nThe host path must use forward slashes. For example, if you placed `rar.exe` in `C:\\Program Files\\RAR`, your volume mount would look like this:\n\n```yaml\n# Example for Windows host path\n- \"C:/Program Files/RAR/rar.exe:/usr/local/bin/rar:ro\"\n```\n\n-----\n\n## Step 3: Verification\n\nConfirm that Stirling PDF can access the `rar` command.\n\n* **For Docker Users:** Execute a command inside the running container.\n\n ```bash\n docker exec -it stirling-pdf rar\n ```\n\n* **For Non-Docker Users:** Check if the `rar` command is recognized in your terminal.\n\n ```bash\n # On Linux and macOS\n which rar\n\n # On Windows\n where rar\n ```\n\nIn both cases, a successful setup will display the RAR version and usage information. An error like \"command not found\" means there is a problem with the installation or `PATH`.\n\n-----\n\n## Important Considerations\n\n### License Note\n\nRAR is shareware. While it is free to use for personal, non-commercial purposes, business or commercial use may require purchasing a license. Please review the official RAR license terms on the RARLAB website for complete details.\n\n### Alternative: Use the CBZ Format\n\nFor broader compatibility and to avoid proprietary software, using the **CBZ (Comic Book ZIP)** format is highly recommended.\n\n* CBZ uses the open and universal ZIP standard.\n* The **PDF to CBZ** tool is enabled in Stirling PDF by default and requires no extra software.\n* CBZ is supported by virtually all modern comic book reader applications.", - "sourcePath": "docs/Advanced Configuration/PDF to CBR Conversion.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Advanced Configuration/PDF to CBR Conversion.md" - }, "analytics-and-telemetry": { "id": "analytics-and-telemetry", "title": "Analytics and Telemetry", "section": "overview", - "markdown": "> Please note all the following applies to version 1.5.0 onward due to be released 16th October\n\n\nStirling‑PDF uses analytics to understand usage patterns and improve the application. This page explains what data is collected, why we collect it, and how to disable analytics if desired.\n\n> **User control**: All analytics are **opt‑in via a consent banner** (disabled until a user allows it). A self‑hosted administrator can also turn all analytics off system‑wide. If analytics are disabled system‑wide, no banner is shown.\n\n## Overview\n\nStirling‑PDF uses two analytics services:\n\n1. **[Scarf](https://scarf.sh)** - a privacy‑friendly tool designed for open‑source projects.\n2. **[PostHog](https://posthog.com)** - an open‑source product analytics platform for detailed usage insights.\n\nBoth services are designed with privacy in mind and can be completely disabled.\n\n---\n\n## PostHog Analytics\n\n### What is PostHog?\n\nPostHog is an open‑source product analytics platform that provides detailed insights into how users interact with Stirling‑PDF. It's hosted on PostHog's European servers (`eu.i.posthog.com`) for GDPR alignment.\n\n### Data collected by PostHog\n\nPostHog collects comprehensive system and usage information **only when analytics are enabled and consented**:\n\n#### System information\n- Operating system name and version\n- Java version and vendor\n- CPU cores and memory allocation\n- Deployment type (Docker, JAR, EXE)\n- Docker/Kubernetes environment details (if applicable)\n- Timezone and locale settings\n\n#### Application configuration\n- Security settings (login enabled, OAuth/SAML configuration status)\n- UI customization settings\n- Feature flags and enabled functionality\n- Legal document URLs (terms, privacy policy, etc.)\n- System limits and quotas\n\n#### Usage data\n- Aggregate counts (e.g., total number of user accounts created)\n- Feature/tool usage (which tools/operations are used)\n- Error tracking\n- Browser and device information (for the web interface)\n\n**Important privacy notes**:\n- **No document content, PDF data, or file metadata is ever collected or transmitted.**\n- PostHog is configured with:\n - `opt_out_capturing_by_default: true`\n - `mask_all_text: true`\n - `mask_all_element_attributes: true`\n- Users must accept cookies before any data is captured.\n- Data is stored on EU servers.\n- Each instance has a unique UUID (not tied to individuals).\n\n### Why we use PostHog\n\nPostHog shows us which features get used, helps us catch bugs, and guides what to build next.\n\n---\n\n\n## Scarf\n\n### What is Scarf?\n\n[Scarf](https://scarf.sh) provides a simple tracking pixel (`pixel.stirling.com`) that collects basic, non‑personally identifiable information about Stirling‑PDF usage.\n\n### Data collected by Scarf\n\nThe Scarf pixel collects the following information:\n\n- **Machine Type**: Deployment type (Docker, JAR, or EXE)\n- **App Version**: The version of Stirling‑PDF you're running\n- **License Type**: Whether you're using Community or Enterprise edition\n- **Login Enabled**: Whether authentication is enabled\n- **Page Endpoint Loaded**: Which Stirling‑PDF page was loaded (e.g., `/split-pdf`)\n\n**Important**: The Scarf pixel does **not** collect or store:\n- Personal information (PII)\n- IP addresses (IP addresses are not stored)\n- User‑specific identifiers\n- Document content or file metadata\n- Fine‑grained user behavior beyond which page/endpoint was loaded\n\n### Why we use Scarf\n\nScarf gives us a rough idea of how Stirling-PDF is deployed and which pages are reached, so we can prioritise work and keep it compatible across setups.\n\n### How to disable Scarf\n\nScarf is opt‑in by default (via the cookie consent banner). To disable the Scarf tracking pixel **system‑wide** and suppress the banner for it:\n\n**Environment variable**\n```bash\nSYSTEM_ENABLESCARF=false\n```\n\n**settings.yml**\n```yaml\nsystem:\n enableScarf: false\n```\n\n---\n\n\n\n## Configuration and Control\n\nAnalytics are governed by a **global master toggle**, **component toggles**, and the **cookie consent banner**.\n\n### 1) Global analytics toggle (master switch)\n\nControls **all** analytics and whether a consent banner appears.\n\n\n \n ```yaml\n system:\n enableAnalytics: false # true | false | null (unset)\n ```\n \n \n ```bash\n SYSTEM_ENABLEANALYTICS=false # true | false | (unset = null)\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n environment:\n SYSTEM_ENABLEANALYTICS: false\n ```\n \n\n\n**Behavior**\n- `false`: Disables **all** analytics (no consent banner; PostHog & Scarf are off).\n- `true`: Allows analytics (banner still required for user consent before any capture).\n- `null`/unset: **First‑run admin choice** - on the first ever connection to a self‑hosted instance, the first visitor (assumed admin) is prompted to choose, and that choice sets the global behavior for all users (either disabling analytics or enabling the consent banner for others).\n\n### 2) Component toggles\n\nUse these to selectively enable/disable providers **in addition** to the global toggle.\n\n**PostHog:**\n\n\n \n ```yaml\n system:\n enablePosthog: false # true | false | null\n ```\n \n \n ```bash\n SYSTEM_ENABLEPOSTHOG=false\n ```\n \n\n\n**Scarf tracking pixel:**\n\n\n \n ```yaml\n system:\n enableScarf: false # true | false | null\n ```\n \n \n ```bash\n SYSTEM_ENABLESCARF=false\n ```\n \n\n\n**Interaction**\n- If `enableAnalytics` is `false`, everything is off regardless of component toggles.\n- If `enableAnalytics` is `true`/`null`, the consent banner is shown (see below). After consent:\n - PostHog runs only if `enablePosthog` is `true`/`null` **and** the user consented.\n - Scarf runs only if `enableScarf` is `true`/`null` **and** the user consented.\n\n### 3) Cookie consent banner\n\nWhen analytics are allowed globally (`system.enableAnalytics: true` or resolved via the first‑run admin choice), users see a cookie consent banner on their first visit. Users can:\n\n- **Accept all** → enables PostHog (if `enablePosthog` is `true`/`null`) and Scarf (if `enableScarf` is `true`/`null`)\n- **Accept only necessary** → disables PostHog and Scarf\n- **Customize** → granular selection where applicable\n\n**User control details**\n- Users can change preferences at any time.\n- Consent choices are stored locally in the user's browser.\n- PostHog and Scarf respect the consent decision immediately.\n- No tracking occurs until explicit consent is given.\n\n---\n\n## Complete analytics disable (shortcut)\n\nIf you want to disable **all** analytics and telemetry (and suppress any consent prompts) at once:\n\n\n \n ```yaml\n system:\n enableAnalytics: false\n ```\n \n \n ```bash\n SYSTEM_ENABLEANALYTICS=false\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n environment:\n SYSTEM_ENABLEANALYTICS: false\n ```\n \n\n\n---\n\n## Privacy and Data Security\n\n### Data retention\n- PostHog data is retained according to PostHog’s configured retention policies.\n- Scarf pixel data is aggregated and anonymized.\n- No personal documents or content are ever transmitted.\n\n### GDPR alignment\n- PostHog servers are located in the EU.\n- Cookie consent is required before tracking.\n- Users can opt out at any time.\n- No cross‑site tracking or fingerprinting.\n- Text and element masking helps prevent accidental PII collection.\n\n### Transparency\n- All analytics‑related code is open source and visible in the repository.\n- Analytics can be completely disabled with simple configuration changes.\n- Users have full control over their data via cookie preferences (when analytics are allowed globally).\n\n---\n\n## For Self‑Hosted Instances\n\nIf you're running Stirling‑PDF on your own infrastructure:\n\n1. **Private networks**: Analytics from self‑hosted instances help us understand deployment patterns but don't expose your internal network.\n2. **Air‑gapped environments**: Disable analytics; the application works perfectly without external connections.\n3. **Corporate environments**: Disable analytics if your security policy requires it, or allow it to help improve the product.\n\n---\n\n## Support\n\nIf you have questions or concerns about analytics:\n\n- Check our [Privacy Policy](https://www.stirling.com/privacy-policy)\n- Review the [source code](https://github.com/Stirling-Tools/Stirling-PDF)\n- Ask questions on [Discord](https://discord.gg/HYmhKj45pU)\n- Open an issue on [GitHub](https://github.com/Stirling-Tools/Stirling-PDF/issues)", + "markdown": "Stirling‑PDF uses analytics to understand usage patterns and improve the application. This page explains what data is collected, why we collect it, and how to disable analytics if desired.\n\n> **User control**: All analytics are **opt‑in via a consent banner** (disabled until a user allows it). A self‑hosted administrator can also turn all analytics off system‑wide. If analytics are disabled system‑wide, no banner is shown.\n\n## Overview\n\nStirling‑PDF uses two analytics services:\n\n1. **[Scarf](https://scarf.sh)** - a privacy‑friendly tool designed for open‑source projects.\n2. **[PostHog](https://posthog.com)** - an open‑source product analytics platform for detailed usage insights.\n\nBoth services are designed with privacy in mind and can be completely disabled.\n\n---\n\n## PostHog Analytics\n\n### What is PostHog?\n\nPostHog is an open‑source product analytics platform that provides detailed insights into how users interact with Stirling‑PDF. It's hosted on PostHog's European servers (`eu.i.posthog.com`) for GDPR alignment.\n\n### Data collected by PostHog\n\nPostHog collects comprehensive system and usage information **only when analytics are enabled and consented**:\n\n#### System information\n- Operating system name and version\n- Java version and vendor\n- CPU cores and memory allocation\n- Deployment type (Docker, JAR, EXE)\n- Docker/Kubernetes environment details (if applicable)\n- Timezone and locale settings\n\n#### Application configuration\n- Security settings (login enabled, OAuth/SAML configuration status)\n- UI customization settings\n- Feature flags and enabled functionality\n- Legal document URLs (terms, privacy policy, etc.)\n- System limits and quotas\n\n#### Usage data\n- Aggregate counts (e.g., total number of user accounts created)\n- Feature/tool usage (which tools/operations are used)\n- Error tracking\n- Browser and device information (for the web interface)\n\n**Important privacy notes**:\n- **No document content, PDF data, or file metadata is ever collected or transmitted.**\n- PostHog is configured with:\n - `opt_out_capturing_by_default: true`\n - `mask_all_text: true`\n - `mask_all_element_attributes: true`\n- Users must accept cookies before any data is captured.\n- Data is stored on EU servers.\n- Each instance has a unique UUID (not tied to individuals).\n\n### Why we use PostHog\n\nPostHog shows us which features get used, helps us catch bugs, and guides what to build next.\n\n---\n\n\n## Scarf\n\n### What is Scarf?\n\n[Scarf](https://scarf.sh) provides a simple tracking pixel (`pixel.stirling.com`) that collects basic, non‑personally identifiable information about Stirling‑PDF usage.\n\n### Data collected by Scarf\n\nThe Scarf pixel collects the following information:\n\n- **Machine Type**: Deployment type (Docker, JAR, or EXE)\n- **App Version**: The version of Stirling‑PDF you're running\n- **License Type**: Whether you're using Community or Enterprise edition\n- **Login Enabled**: Whether authentication is enabled\n- **Page Endpoint Loaded**: Which Stirling‑PDF page was loaded (e.g., `/split-pdf`)\n\n**Important**: The Scarf pixel does **not** collect or store:\n- Personal information (PII)\n- IP addresses (IP addresses are not stored)\n- User‑specific identifiers\n- Document content or file metadata\n- Fine‑grained user behavior beyond which page/endpoint was loaded\n\n### Why we use Scarf\n\nScarf gives us a rough idea of how Stirling-PDF is deployed and which pages are reached, so we can prioritise work and keep it compatible across setups.\n\n### How to disable Scarf\n\nScarf is opt‑in by default (via the cookie consent banner). To disable the Scarf tracking pixel **system‑wide** and suppress the banner for it:\n\n**Environment variable**\n```bash\nSYSTEM_ENABLESCARF=false\n```\n\n**settings.yml**\n```yaml\nsystem:\n enableScarf: false\n```\n\n---\n\n\n\n## Configuration and Control\n\nAnalytics are governed by a **global master toggle**, **component toggles**, and the **cookie consent banner**.\n\n### 1) Global analytics toggle (master switch)\n\nControls **all** analytics and whether a consent banner appears.\n\n\n \n ```yaml\n system:\n enableAnalytics: false # true | false | null (unset)\n ```\n \n \n ```bash\n SYSTEM_ENABLEANALYTICS=false # true | false | (unset = null)\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n environment:\n SYSTEM_ENABLEANALYTICS: false\n ```\n \n\n\n**Behavior**\n- `false`: Disables **all** analytics (no consent banner; PostHog & Scarf are off).\n- `true`: Allows analytics (banner still required for user consent before any capture).\n- `null`/unset: **First‑run admin choice** - on the first ever connection to a self‑hosted instance, the first visitor (assumed admin) is prompted to choose, and that choice sets the global behavior for all users (either disabling analytics or enabling the consent banner for others).\n\n### 2) Component toggles\n\nUse these to selectively enable/disable providers **in addition** to the global toggle.\n\n**PostHog:**\n\n\n \n ```yaml\n system:\n enablePosthog: false # true | false | null\n ```\n \n \n ```bash\n SYSTEM_ENABLEPOSTHOG=false\n ```\n \n\n\n**Scarf tracking pixel:**\n\n\n \n ```yaml\n system:\n enableScarf: false # true | false | null\n ```\n \n \n ```bash\n SYSTEM_ENABLESCARF=false\n ```\n \n\n\n**Interaction**\n- If `enableAnalytics` is `false`, everything is off regardless of component toggles.\n- If `enableAnalytics` is `true`/`null`, the consent banner is shown (see below). After consent:\n - PostHog runs only if `enablePosthog` is `true`/`null` **and** the user consented.\n - Scarf runs only if `enableScarf` is `true`/`null` **and** the user consented.\n\n### 3) Cookie consent banner\n\nWhen analytics are allowed globally (`system.enableAnalytics: true` or resolved via the first‑run admin choice), users see a cookie consent banner on their first visit. Users can:\n\n- **Accept all** → enables PostHog (if `enablePosthog` is `true`/`null`) and Scarf (if `enableScarf` is `true`/`null`)\n- **Accept only necessary** → disables PostHog and Scarf\n- **Customize** → granular selection where applicable\n\n**User control details**\n- Users can change preferences at any time.\n- Consent choices are stored locally in the user's browser.\n- PostHog and Scarf respect the consent decision immediately.\n- No tracking occurs until explicit consent is given.\n\n---\n\n## Complete analytics disable (shortcut)\n\nIf you want to disable **all** analytics and telemetry (and suppress any consent prompts) at once:\n\n\n \n ```yaml\n system:\n enableAnalytics: false\n ```\n \n \n ```bash\n SYSTEM_ENABLEANALYTICS=false\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n environment:\n SYSTEM_ENABLEANALYTICS: false\n ```\n \n\n\n---\n\n## Privacy and Data Security\n\n### Data retention\n- PostHog data is retained according to PostHog’s configured retention policies.\n- Scarf pixel data is aggregated and anonymized.\n- No personal documents or content are ever transmitted.\n\n### GDPR alignment\n- PostHog servers are located in the EU.\n- Cookie consent is required before tracking.\n- Users can opt out at any time.\n- No cross‑site tracking or fingerprinting.\n- Text and element masking helps prevent accidental PII collection.\n\n### Transparency\n- All analytics‑related code is open source and visible in the repository.\n- Analytics can be completely disabled with simple configuration changes.\n- Users have full control over their data via cookie preferences (when analytics are allowed globally).\n\n---\n\n## For Self‑Hosted Instances\n\nIf you're running Stirling‑PDF on your own infrastructure:\n\n1. **Private networks**: Analytics from self‑hosted instances help us understand deployment patterns but don't expose your internal network.\n2. **Air‑gapped environments**: Disable analytics; the application works perfectly without external connections.\n3. **Corporate environments**: Disable analytics if your security policy requires it, or allow it to help improve the product.\n\n---\n\n## Support\n\nIf you have questions or concerns about analytics:\n\n- Check our [Privacy Policy](https://www.stirling.com/privacy-policy)\n- Review the [source code](https://github.com/Stirling-Tools/Stirling-PDF)\n- Ask questions on [Discord](https://discord.gg/HYmhKj45pU)\n- Open an issue on [GitHub](https://github.com/Stirling-Tools/Stirling-PDF/issues)", "sourcePath": "docs/Analytics-and-telemetry.md", "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Analytics-and-telemetry.md" }, - "configuration/audit-logging": { - "id": "configuration/audit-logging", - "title": "Audit Logging", - "section": "configuration", - "markdown": "> **Tier**: Enterprise\n\nLogs every operation, who ran it, what tool, which files, when. All data is stored in the database.\nWe recommend external database setup when using this feature due to the potential volume.\nPlease note the data stored is customisable based on you and your organisations needs and legal requirements.\n\n\nSettings are under `premium.enterpriseFeatures.audit`.\n\n| Setting | Default | Description |\n|---|---|---|\n| `enabled` | `true` | Turn audit logging on or off |\n| `level` | `2` | Verbosity: `0` = off, `1` = basic, `2` = standard, `3` = verbose |\n| `retentionDays` | `90` | Days to keep audit records before purging (`0` = infinite retention) |\n| `captureFileHash` | `false` | Store a SHA-256 hash of each processed file |\n| `capturePdfAuthor` | `false` | Extract and store PDF author metadata |\n| `captureOperationResults` | `false` | Store operation return values - high volume, use sparingly |\n\n## Audit levels\n\n| Level | What's recorded |\n|---|---|\n| `0` - OFF | Nothing |\n| `1` - BASIC | File modifications only - PDF operations (compress, split, merge, etc.) and settings changes |\n| `2` - STANDARD | BASIC + user actions (login/logout, account changes, general GET requests) |\n| `3` - VERBOSE | STANDARD + continuous polling calls and all GET requests |\n\n## Example\n\n\n \n ```yaml\n premium:\n enabled: true\n key: your-enterprise-license-key\n enterpriseFeatures:\n audit:\n enabled: true\n level: 2\n retentionDays: 365\n captureFileHash: true\n capturePdfAuthor: false\n captureOperationResults: false\n ```\n \n \n ```bash\n PREMIUM_ENABLED=true\n PREMIUM_KEY=your-enterprise-license-key\n PREMIUM_ENTERPRISEFEATURES_AUDIT_ENABLED=true\n PREMIUM_ENTERPRISEFEATURES_AUDIT_LEVEL=2\n PREMIUM_ENTERPRISEFEATURES_AUDIT_RETENTIONDAYS=365\n PREMIUM_ENTERPRISEFEATURES_AUDIT_CAPTUREFILEHASH=true\n PREMIUM_ENTERPRISEFEATURES_AUDIT_CAPTUREPDFAUTHOR=false\n PREMIUM_ENTERPRISEFEATURES_AUDIT_CAPTUREOPERATIONRESULTS=false\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n environment:\n PREMIUM_ENABLED: true\n PREMIUM_KEY: your-enterprise-license-key\n PREMIUM_ENTERPRISEFEATURES_AUDIT_ENABLED: true\n PREMIUM_ENTERPRISEFEATURES_AUDIT_LEVEL: 2\n PREMIUM_ENTERPRISEFEATURES_AUDIT_RETENTIONDAYS: 365\n PREMIUM_ENTERPRISEFEATURES_AUDIT_CAPTUREFILEHASH: true\n ```\n \n\n\n> **Note on performance:** `captureFileHash` adds a SHA-256 calculation for every file processed - noticeable overhead at high volume. `captureOperationResults` stores full operation output in the database and can grow very large; only enable it when specifically needed.", - "sourcePath": "docs/Configuration/Audit Logging.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Audit Logging.md" + "configuration/automation/mcp-server": { + "id": "configuration/automation/mcp-server", + "title": "MCP Server", + "description": "Expose Stirling PDF's tools to MCP clients over a built-in Model Context Protocol server", + "section": "configuration/automation", + "markdown": "Stirling PDF ships a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server. MCP is the open standard MCP clients (Claude Desktop, the MCP Inspector, IDE agents, and custom tools) use to discover and call tools on a remote server. When enabled, Stirling PDF exposes its PDF operations as MCP tools so an MCP-capable assistant can run them on your behalf.\n\nThe MCP server is built into the Stirling PDF self-hosted server and the desktop app in Local / Self-hosted modes. It is **off by default** and must be enabled and configured per deployment.\n\n> **ℹ️ Info: Self-hosted capability**\n>\n> This page documents the MCP server you run on your own Stirling PDF instance. The per-user MCP tab in Stirling Cloud is a separate, cloud-only surface and is not covered here. For where each deployment mode applies, see [Modes](doc:modes-and-licensing).\n\n\n---\n\n## Enable the server\n\nThe MCP server runs only when `mcp.enabled` is `true`. While off, there is no `/mcp` endpoint and no MCP metadata.\n\nTurn it on either way:\n\n- **Settings file / environment variable**: set `mcp.enabled: true` in `settings.yml`, or the environment variable `MCP_ENABLED=true`. A restart applies file edits.\n- **Admin UI**: open **Admin Settings → MCP Server**. This page is shown to admins, and on instances where login is disabled. Saving prompts for a restart.\n\nEnabling alone is not enough - you also need to choose and configure an [authentication mode](#authentication) before clients can call tools.\n\n---\n\n## Transport and protocol\n\n- **Endpoint**: `POST /mcp` on the same host and port as the rest of Stirling PDF.\n- **Protocol**: JSON-RPC 2.0 over streamable-HTTP.\n- **Supported MCP protocol versions**: `2025-06-18` (preferred), `2025-03-26`, and `2024-11-05`. The server echoes the client's requested version when it is supported, otherwise it advertises the preferred version.\n\n---\n\n## Tools exposed\n\nThe server presents a small set of category tools rather than one tool per operation. An MCP client lists them, then drills into a specific PDF operation using `stirling_describe_operation`.\n\n| Tool | Purpose |\n|---|---|\n| `stirling_describe_operation` | Look up the parameters and JSON schema for a specific operation id. |\n| `stirling_pages` | Page-level operations (merge, split, rotate, reorder, add blank pages, and similar). |\n| `stirling_convert` | Conversions to and from PDF (images, office formats, and similar). |\n| `stirling_misc` | Miscellaneous utilities (compress, flatten, repair, and similar). |\n| `stirling_security` | Security operations (encrypt, decrypt, permissions, and similar). |\n| `stirling_upload` | Store a file server-side and get back a `fileId` for large inputs. |\n| `stirling_download` | Fetch a result that was returned by reference rather than inline. |\n| `stirling_ai` | AI-engine capabilities. **Not usable in self-hosted - see the caveat below.** |\n\n> **⚠️ Warning: `stirling_ai` requires a Stirling Cloud AI engine**\n>\n> `stirling_ai` only has capabilities when a Stirling AI engine is configured. The AI engine is a Stirling Cloud feature and is **not available in self-hosted** today, so on a self-hosted server `stirling_ai` exposes nothing and only the PDF tools above are usable over MCP.\n\n\n---\n\n## Authentication\n\nPick one of two modes with `mcp.auth.mode`.\n\n### OAuth2 resource server (`oauth`, default)\n\nIn OAuth mode the `/mcp` endpoint runs as an OAuth2 resource server: it validates incoming JWTs (signature, issuer, expiry, and audience) and binds each token to an existing Stirling account. It publishes RFC 9728 protected-resource metadata at `/.well-known/oauth-protected-resource` so MCP clients can discover the authorization server.\n\n| Key | Env | Default | Purpose |\n|---|---|---|---|\n| `mcp.auth.issuerUri` | `MCP_AUTH_ISSUERURI` | empty | OAuth2 issuer URI (e.g. `http://localhost:9000`). **Required** in OAuth mode; every token is rejected until it is set. |\n| `mcp.auth.jwksUri` | `MCP_AUTH_JWKSURI` | empty | JWKS URI. Blank means it is derived from the issuer's `/.well-known/openid-configuration`. |\n| `mcp.auth.resourceId` | `MCP_AUTH_RESOURCEID` | empty | RFC 8707 resource identifier of this server. Must equal the public `/mcp` URL clients call and **end in `/mcp`** (e.g. `http://localhost:8080/mcp`). Tokens that do not list it in `aud` are rejected. |\n| `mcp.auth.acceptedAudiences` | `MCP_AUTH_ACCEPTEDAUDIENCES` | `[]` | Extra `aud` values accepted on top of `resourceId`. Empty keeps strict RFC 8707 binding. Use this for IdPs that cannot mint a resource-specific audience (e.g. Supabase always issues `aud=authenticated`). |\n| `mcp.auth.usernameClaim` | `MCP_AUTH_USERNAMECLAIM` | `sub` | JWT claim matched against a Stirling username. Set to `email` or `preferred_username` if your IdP maps users differently. |\n| `mcp.auth.requireExistingAccount` | `MCP_AUTH_REQUIREEXISTINGACCOUNT` | `true` | Reject tokens whose subject has no enabled Stirling account. Keep `true` unless you intend open access for any IdP-valid token. |\n| `mcp.scopesEnabled` | `MCP_SCOPESENABLED` | `true` | Enforce the `mcp.tools.read` / `mcp.tools.write` scopes. Read-style tools require `mcp.tools.read`; mutating operations require `mcp.tools.write`. Set `false` only if your IdP can issue a single coarse token. |\n\nEach validated token is bound to the matching Stirling account so that audit and attribution are correct.\n\n### API key (`apikey`)\n\nAPI-key mode is the low-friction option for self-hosters with no external identity provider. Set `mcp.auth.mode: apikey` (env `MCP_AUTH_MODE=apikey`) and clients authenticate with an existing per-user Stirling API key.\n\nSend the key as either header:\n\n```text\nX-API-KEY: \n```\n\nor\n\n```text\nAuthorization: Bearer \n```\n\nThe key must belong to an existing, enabled account (generate one under **Account → API Keys** - see [API documentation](doc:api)). No external IdP, OAuth, or JWKS configuration is needed.\n\n---\n\n## Restrict which operations are exposed\n\nTwo MCP-level lists control which operations clients can see and call. They use the same kebab-case operation ids as the [Endpoint or Feature Customisation](doc:configuration/customisation/endpoint-or-feature-customisation) page (e.g. `compress-pdf`).\n\n| Key | Env | Default | Behaviour |\n|---|---|---|---|\n| `mcp.allowedOperations` | `MCP_ALLOWEDOPERATIONS` | `[]` | When **non-empty**, acts as a strict allow-list - only these operations are exposed over MCP; everything else is hidden, undescribable, and uninvocable. Empty means allow all. |\n| `mcp.blockedOperations` | `MCP_BLOCKEDOPERATIONS` | `[]` | A deny-list. Anything listed is always removed, applied **after** the allow-list, so a blocked id wins even if it is also allowed. |\n\nThese lists layer **on top of** the global [`endpoints.toRemove` / `endpoints.groupsToRemove`](doc:configuration/customisation/endpoint-or-feature-customisation) configuration. An operation disabled globally is never exposed over MCP regardless of these lists.\n\n---\n\n## Limits\n\n| Key | Env | Default | Purpose |\n|---|---|---|---|\n| `mcp.maxRequestBytes` | `MCP_MAXREQUESTBYTES` | 10 MB | Maximum MCP request body size. Inline file uploads ride in the JSON-RPC body, so this caps how large an inline input can be. |\n| `mcp.maxInlineResponseBytes` | `MCP_MAXINLINERESPONSEBYTES` | 10 MB | Results up to this size return inline as base64; larger results return a `fileId` instead, which the client fetches with `stirling_download`. |\n| `mcp.engineCapabilityRefreshMinutes` | `MCP_ENGINECAPABILITYREFRESHMINUTES` | `5` | How often the AI capabilities manifest is refreshed from the engine (only relevant when an AI engine is available). |\n\n---\n\n## Troubleshooting and operability\n\n- **Startup config validation**: when MCP is enabled, the server validates the resolved config at boot and logs findings. Misconfiguration (missing issuer, a `resourceId` that does not end in `/mcp`, a `username-claim` of `sub` with `require-existing-account=true`, and similar) is logged as a warning so it surfaces in the logs instead of as a later rejected-token `401`.\n- **Meaningful `401` responses**: a rejected OAuth token returns a `WWW-Authenticate` header carrying a real `error_description` (audience, issuer, or expiry mismatch), plus a `resource_metadata` pointer for discovery. A tokenless `401` is the normal discovery handshake, not an error.\n- **Audit logging**: MCP calls are attributed to the bound Stirling account, and no secret is written to the audit log.\n\n---\n\n## Connect a client\n\nPoint any MCP client at `http://your-host:8080/mcp` (use your real host, port, and scheme).\n\n**MCP Inspector** (quick manual testing):\n\n```bash\nnpx @modelcontextprotocol/inspector\n```\n\nThen set the transport to streamable-HTTP and the URL to your `/mcp` endpoint, adding the appropriate auth header (`X-API-KEY` in API-key mode, or a Bearer token in OAuth mode).\n\n**Claude Desktop** via the `mcp-remote` bridge - add to your Claude Desktop config:\n\n```json\n{\n \"mcpServers\": {\n \"stirling-pdf\": {\n \"command\": \"npx\",\n \"args\": [\n \"-y\",\n \"mcp-remote\",\n \"http://your-host:8080/mcp\",\n \"--header\",\n \"X-API-KEY:your-stirling-api-key\"\n ]\n }\n }\n}\n```\n\nIn OAuth mode, drop the `X-API-KEY` header and let `mcp-remote` complete the OAuth flow against your configured issuer.\n\n---\n\n## Related Documentation\n\n- **[API documentation](doc:api)** - generate the per-user API key used in API-key mode\n- **[Endpoint or Feature Customisation](doc:configuration/customisation/endpoint-or-feature-customisation)** - the operation ids and global enable/disable config the MCP lists build on\n- **[Modes](doc:modes-and-licensing)** - where each deployment mode and feature applies", + "sourcePath": "docs/Configuration/Automation/MCP-Server.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Automation/MCP-Server.md" + }, + "configuration/automation/pipeline": { + "id": "configuration/automation/pipeline", + "title": "Pipeline Automation (Automate)", + "description": "Create automated multi-step PDF workflows with the Automate tool", + "section": "configuration/automation", + "markdown": "Create powerful automated workflows that combine multiple PDF operations into sequential processes. The Automate tool (formerly called \"Pipeline\") lets you build, save, and reuse complex PDF processing workflows.\n\n> **ℹ️ Info: V2.0 Update - New \"Automate\" Feature**\n>\n> In V2.0, the pipeline frontend interface has been redesigned as the **\"Automate\"** feature with an improved user experience for creating and managing automation workflows. The backend pipeline system (JSON configuration and folder scanning) continues to work the same way.\n>\n> **What changed:**\n> - Backend pipeline processing - **No changes**\n> - JSON pipeline configurations - **Still work exactly the same**\n> - Folder scanning with pipelines - **Still works the same**\n> - Frontend interface - **Now called \"Automate\" with better UX**\n>\n> If you have existing pipeline JSON files, they continue to work in V2.0's Automate feature.\n\n\n---\n\n## What is Pipeline Automation?\n\nPipeline automation allows you to:\n- **Chain operations** - Combine multiple PDF tools in sequence\n- **Save workflows** - Reuse common operation sequences\n- **Automate processing** - Process files automatically with folder scanning\n- **Standardize procedures** - Ensure consistent processing across teams\n- **Batch process** - Apply same workflow to multiple files\n\nThink of it as **\"macros for PDFs\"** - record your steps once, replay them unlimited times.\n\n---\n\n## Why Use Pipelines?\n\n### Without Pipelines:\n1. Upload PDF to Split tool, download split files\n2. Upload each split file to Watermark tool, download watermarked files\n3. Upload each watermarked file to Compress tool, download final files\n4. Repeat for every batch of documents\n\n### With Pipelines:\n1. Create \"Split-Watermark-Compress\" pipeline once\n2. Upload PDFs, automatic processing, download results\n3. Reuse same pipeline for all future batches\n\n**Time saved:** Minutes per file, hours per day.\n\n---\n\n## Key Concepts\n\n### Operations\nIndividual PDF tools that perform specific tasks:\n- Split, Merge, Compress, Watermark, etc.\n- Each operation has configurable parameters\n- Operations execute in the order you define\n\n### Pipeline\nA sequence of operations with saved configurations:\n- Named workflow (e.g., \"Invoice Processing\")\n- Ordered list of operations\n- Pre-configured settings for each operation\n- Reusable across multiple files\n\n### Pipeline Configuration (JSON)\nText file that defines your pipeline:\n- Lists operations in order\n- Specifies parameters for each operation\n- Can be shared, versioned, and backed up\n- Human-readable and editable\n\n### Folder Scanning\nAutomated processing mode:\n- Watch a folder for new files\n- Automatically apply pipeline to new files\n- Move processed files to output folder\n- Unattended batch processing\n\n---\n\n## Getting Started with Automate\n\n### Accessing the Automate Tool\n\n1. **From Home Page**\n - Click \"Automate\" in the Advanced Tools section\n - Or search for \"automate\" or \"pipeline\"\n\n2. **Open the Automation Builder**\n - Click \"Create New Automation\"\n - The automation builder opens\n\n---\n\n## Building Your First Pipeline\n\n## Steps to Configure and Use Your Pipeline\n\n1. **Start a New Automation**\n - On the Automate screen, click **Create New Automation**.\n\n2. **Enter Automation Name**\n - Provide a name for your automation in the designated field (you can also add an optional description and icon).\n\n3. **Add Tools**\n - Choose the tools for your automation (e.g., **Split Pages**) with **Add Tool**. Tools run in the order you add them.\n\n4. **Configure Tool Settings**\n - Configure each added tool. A tool shows a **! Not Configured** marker until its settings are saved.\n\n5. **Add More Tools**\n - You can add and reorder multiple tools. Make sure each tool is configured.\n\n6. **Save Tool Settings**\n - Click **Save Configuration** in each tool's settings dialog after customizing it.\n\n7. **Save the Automation**\n - Click **Save Automation** once all tools are configured. The Save button stays disabled until the automation is complete.\n\n8. **Download Pipeline Configuration**\n - The Automate UI offers two download buttons:\n - **Export** - downloads `.automate.json` in the **native Automate format** with frontend tool IDs. Use this for re-importing into another Stirling PDF instance via the UI.\n - **Export for Folder Scanning** - downloads `.folder-scan.json` in the **backend format** with full endpoint paths. Use this for the REST API and for folder scanning.\n - To pre-load a pipeline for all users, place a folder-scanning-format JSON file in `/pipeline/defaultWebUIConfigs/` - it will appear in the dropdown.\n\n9. **Run the Automation**\n - Once saved, select the automation from the list, add your files, and run it.\n\n10. **Note on Web UI Limitations**\n - The current web UI version does not support operations that require multiple different types of inputs, such as adding a separate image to a PDF.\n\n### Current Limitations\n\n- Cannot have more than one of the same operation.\n- Cannot input additional files via UI.\n- All files and operations run in serial mode.\n\n---\n\n## Example Pipelines\n\n### Example 1: Invoice Processing\n**Goal:** Process scanned invoices for archival\n\n**Pipeline Steps:**\n1. **OCR** - Make invoices searchable\n - Language: English\n - Preserve formatting: Yes\n2. **Crop** - Remove scanner edges\n - Margins: 0.5 inches all sides\n3. **Add Watermark** - Mark as processed\n - Text: \"PROCESSED [DATE]\"\n - Position: Bottom right\n - Opacity: 50%\n4. **Compress** - Reduce file size\n - Level: Medium\n5. **Add Password** - Secure documents\n - Password: [configured per run]\n\n**Use Case:** Accounting department processing hundreds of invoices monthly\n\n---\n\n### Example 2: Report Distribution\n**Goal:** Prepare reports for external sharing\n\n**Pipeline Steps:**\n1. **Remove Pages** - Remove internal pages\n - Pages: 2,3 (remove cover sheets)\n2. **Add Page Numbers** - Number all pages\n - Position: Bottom center\n - Format: \"Page X of Y\"\n3. **Add Stamp** - Add \"CONFIDENTIAL\" stamp\n - Position: Top right\n - Color: Red\n4. **Change Permissions** - Restrict editing\n - Allow printing: Yes\n - Allow editing: No\n5. **Compress** - Optimize for email\n - Level: High\n\n**Use Case:** Monthly reports sent to clients\n\n---\n\n### Example 3: Document Standardization\n**Goal:** Standardize format of received documents\n\n**Pipeline Steps:**\n1. **Rotate** - Fix orientation\n - Mode: Auto-detect\n2. **Scale Pages** - Standardize to Letter size\n - Target: 8.5 x 11 inches\n3. **Add Metadata** - Tag documents\n - Title: [Auto-extracted]\n - Author: \"Company Name\"\n - Keywords: \"Standardized, Processed\"\n4. **Remove Annotations** - Clean markup\n5. **Flatten** - Remove form fields\n\n**Use Case:** HR department standardizing employee submissions\n\n---\n\n### Example 4: Batch Conversion\n**Goal:** Convert and optimize image scans\n\n**Pipeline Steps:**\n1. **Convert** - Images to PDF\n - Source: JPG, PNG\n2. **OCR** - Add text layer\n - Language: Multiple\n3. **Remove Blanks** - Delete empty pages\n - Threshold: 95%\n4. **Compress** - Optimize size\n - Level: Medium\n5. **PDF/A** - Convert for archival\n - Version: PDF/A-2b\n\n**Use Case:** Digitization project for paper archives\n\n---\n\n## Common Pipeline Patterns\n\n### Quality Enhancement Pipeline\n**Pattern:** Improve scanned document quality\n```\nOCR → Remove Blanks → Adjust Contrast → Compress → Add Metadata\n```\n\n### Security Pipeline\n**Pattern:** Secure documents for distribution\n```\nRemove Metadata → Add Watermark → Add Password → Change Permissions\n```\n\n### Compression Pipeline\n**Pattern:** Reduce file sizes for storage/email\n```\nRemove Annotations → Remove Images (optional) → Compress → Validate\n```\n\n### Branding Pipeline\n**Pattern:** Add company branding to documents\n```\nAdd Watermark → Add Stamp → Add Page Numbers → Add Metadata\n```\n\n### Preparation Pipeline\n**Pattern:** Prepare documents for printing\n```\nRotate → Scale Pages → Booklet Imposition → Remove Annotations\n```\n\n---\n\n## JSON Configuration\n\n### Basic Structure\n\nA pipeline JSON file has a `name` and a `pipeline` array. Each entry has an `operation` (the full API endpoint path) and a `parameters` object:\n\n```json\n{\n \"name\": \"My Pipeline\",\n \"pipeline\": [\n {\n \"operation\": \"/api/v1/general/split-pages\",\n \"parameters\": {\n \"pageNumbers\": \"5\"\n }\n },\n {\n \"operation\": \"/api/v1/misc/compress-pdf\",\n \"parameters\": {\n \"optimizeLevel\": 5,\n \"expectedOutputSize\": \"\"\n }\n }\n ]\n}\n```\n\n> **📝 Note: Operation names are full endpoint paths**\n>\n> Pipeline operation names use the **full REST API path**, not short names. For example, use `/api/v1/general/split-pages` (not just `split-pages`). The Folder-Scanning export from the Automate UI produces these paths automatically.\n>\n> If you have older pipeline JSONs that used short names, regenerate them from the Automate UI using **Export for Folder Scanning**.\n\n\n### Optional fields\n\nFor folder scanning (not used by the REST API):\n\n```json\n{\n \"name\": \"...\",\n \"pipeline\": [ ... ],\n \"outputDir\": \"{outputFolder}/{folderName}\",\n \"outputFileName\": \"{filename}-{pipelineName}-{date}-{time}\"\n}\n```\n\n`outputDir` and `outputFileName` accept the placeholders `{outputFolder}`, `{folderName}`, `{filename}`, `{pipelineName}`, `{date}`, `{time}`.\n\n---\n\n## Operation and parameter reference\n\nPipeline operations use the **full endpoint paths** of Stirling PDF's REST API, with the same field names. So once you know the underlying endpoint, you know the pipeline operation - no separate vocabulary to learn.\n\nFor the canonical list of operations and the full parameter schema for each, see:\n\n- **Local Swagger UI** at `/swagger-ui.html` on your instance - includes every endpoint, parameter types, and lets you try requests live\n- **Online API reference** - the [Stirling PDF API documentation](https://app.swaggerhub.com/apis-docs/Frooodle/Stirling-PDF/) and the [Scalar API registry](https://registry.scalar.com/@stirlingpdf/apis/stirling-pdf-processing-api/)\n\nSee [API Documentation](doc:api) for authentication and general API usage.\n\nPipelines can only call endpoints under `/api/v1/general/...`, `/api/v1/misc/...`, `/api/v1/security/...`, `/api/v1/convert/...`, `/api/v1/filter/...`, and `/api/v1/ai/tools/...`. Anything outside those namespaces is rejected by the pipeline processor with a `SecurityException` - this includes `/api/v1/info/...`, `/api/v1/auth/...`, `/api/v1/admin/...`, and `/api/v1/pipeline/handleData` itself (pipelines cannot recursively call themselves).\n\nThe `/api/v1/ai/tools/...` namespace currently exposes proprietary AI features (e.g. `math-auditor-agent`, `pdf-comment-agent`) and is only available with the corresponding paid license.\n\n> **💡 Tip: Build it in the UI, export it as JSON**\n>\n> The fastest way to get a correct pipeline JSON for any combination of operations is to build it visually in the **Automate** tool and click **Export for Folder Scanning**. The exported file uses exactly the format the API expects, with the right operation paths and parameters already filled in for you.\n\n\n---\n\n## Filter / conditional operations\n\nFilter operations let you **branch a pipeline**. Each one checks a property of the file and either lets the file **continue to the next steps** or **drops it** so the rest of the pipeline never sees it. This is how you say \"only keep processing files that match X\" inside an automation - for example, only run OCR on scans that have no text yet, or only watermark documents over a certain page count.\n\nA file that does not match a filter is simply removed from the rest of the pipeline. It is not treated as an error.\n\nThese operation names go in your pipeline configuration:\n\n| Operation name | Keeps the file when... |\n|---|---|\n| `filter-contains-text` | the PDF contains a given piece of text (you can limit the check to specific pages) |\n| `filter-contains-image` | the PDF contains an image (you can limit the check to specific pages) |\n| `filter-page-count` | the page count is greater than, equal to, or less than a value you set |\n| `filter-page-size` | the first page's size compares to a standard page size you choose |\n| `filter-file-size` | the file size compares to a value you set |\n| `filter-page-rotation` | the first page's rotation compares to a value you set |\n\nThe four comparison filters (`filter-page-count`, `filter-page-size`, `filter-file-size`, `filter-page-rotation`) take a `comparator` of `Greater`, `Equal`, or `Less`.\n\n**Example - only OCR files that are image-only scans:** detect scans with no text layer using `filter-contains-image`, then route the matching files through the OCR operation. Files that already contain text are dropped before the OCR step, so you only spend processing time on the scans that need it.\n\n```json\n{\n \"name\": \"OCR only image-only scans\",\n \"pipeline\": [\n {\"operation\": \"/api/v1/filter/filter-contains-image\", \"parameters\": {\"pageNumbers\": \"all\"}},\n {\"operation\": \"/api/v1/misc/ocr-pdf\", \"parameters\": {\"languages\": [\"eng\"], \"ocrType\": \"skip-text\"}}\n ]\n}\n```\n\nThese filter operations work both in the REST API and in folder scanning.\n\n---\n\n\n## REST API: `POST /api/v1/pipeline/handleData`\n\nTrigger a pipeline programmatically via the REST API. Use this from scripts, automation platforms (n8n, Zapier, Make, Power Automate), or your own integrations.\n\n### Request\n\n- **Method**: `POST`\n- **URL**: `/api/v1/pipeline/handleData`\n- **Content-Type**: `multipart/form-data`\n- **Authentication**: When security is enabled, set the `X-API-KEY` header. See [API Documentation](doc:api) for details.\n\n### Multipart fields\n\n| Field | Type | Required | Purpose |\n|---|---|---|---|\n| `fileInput` | file | yes | One or more PDF files. Repeat the field for multiple files. |\n| `json` | string | yes | The pipeline configuration JSON. |\n\nYou don't need to include `fileInput` inside the `parameters` object - the pipeline processor injects each uploaded file automatically. The Automate UI's \"Export for Folder Scanning\" includes `\"fileInput\": \"automated\"` as a marker in every step, which the backend ignores; you can leave it in or strip it out, both work.\n\n### Optional query parameters\n\n- `?async=true` - run the pipeline asynchronously and return a job ID instead of the file. Poll `GET /api/v1/general/job/{id}` for progress.\n\n### Response\n\n- **Single output file**: returned directly as `application/octet-stream` with `Content-Disposition: attachment; filename=...`.\n- **Multiple output files**: returned as `output.zip`.\n- **Async mode**: returns a JSON body with the job ID.\n\n### Working curl example\n\n```bash\ncurl -X POST \"http://localhost:8080/api/v1/pipeline/handleData\" \\\n -H \"X-API-KEY: $STIRLING_API_KEY\" \\\n -F \"fileInput=@/path/to/input.pdf\" \\\n -F 'json={\n \"name\": \"Repair-then-compress\",\n \"pipeline\": [\n {\"operation\": \"/api/v1/misc/repair\", \"parameters\": {}},\n {\"operation\": \"/api/v1/misc/compress-pdf\", \"parameters\": {\"optimizeLevel\": 2}}\n ]\n }' \\\n --output result.pdf\n```\n\nFor multiple files use repeated `-F \"fileInput=@...\"` flags; the response will be `output.zip`. For the full parameter list for each operation, see the API docs linked above.\n\n### Error responses\n\n| Situation | HTTP status | Body |\n|---|---|---|\n| Auth required and no key supplied | 401 | `{\"error\":\"Unauthorized\",\"message\":\"Authentication required...\",\"status\":401}` |\n| Multipart parsing failed (missing field, bad JSON) | 400 | Spring's standard error JSON |\n| Invalid operation name, disallowed endpoint, or missing required parameter | 200 with empty body | The server logs an `IllegalArgumentException` but returns an empty response. |\n| Downstream endpoint returned non-2xx | 200 with partial/empty body | The error is logged but does not surface in the HTTP response. |\n\n> **⚠️ Warning: Validate response bodies**\n>\n> Errors that occur after multipart parsing currently collapse to `HTTP 200` with an empty body. Always check that the response is a non-empty PDF (starts with `%PDF-`) or a ZIP (starts with `PK\\x03\\x04`) before treating the call as successful.\n\n\n### Tips\n\n- **Build in the UI, export the JSON.** The fastest way to get a correct JSON is to build the pipeline in the Automate UI, then click **Export for Folder Scanning**. The exported file works directly with `handleData`. The other button, **Export**, produces a different \"native Automate\" format (uses an `operations` key with frontend tool IDs like `\"merge\"`) that is only for re-importing into another Automate UI, not for the API.\n- **No image / file parameters.** Operations that take an additional file input (image watermarks, separate overlay PDFs, attaching files) cannot be expressed in pipeline JSON via the REST API. Call those endpoints directly instead.\n- **List parameters become repeated form fields.** Internally the processor expands `[\"eng\",\"deu\"]` into two `languages=eng` and `languages=deu` form parts, which is what the underlying endpoints expect.\n- **Filters drop files.** A filter step that doesn't match keeps the file out of later steps. Useful for \"process only PDFs that contain X\".\n- **Multi-input operations batch.** Operations marked multi-input (e.g. `merge-pdfs`) receive every matching file in a single call. If no files in the working set match the operation's expected extension, the step logs `No files with extension X found for operation Y...` and continues with the other files.\n- **Unknown JSON fields are ignored.** The pipeline parser silently drops fields it doesn't recognise, so you can add `description`, `icon`, or other metadata at the top level without breaking anything.\n\n---\n\n## Folder Scanning Setup\n\nAutomate processing of files placed in watched folders.\n\n### How Folder Scanning Works\n\n1. **Watch Input Folder** - Monitor for new files\n2. **Detect New Files** - Identify PDFs added to folder\n3. **Apply Pipeline** - Process with configured pipeline\n4. **Output Results** - Save to output folder\n5. **Archive Originals** - Move processed files (optional)\n\n### Directory Structure\n\n```\n/pipeline/\n ├── watchedFolders/\n │ ├── invoice-processing/\n │ │ ├── my-pipeline.json # any *.json file in the folder is the pipeline config\n │ │ ├── invoice-001.pdf # drop PDFs directly into the folder root\n │ │ ├── invoice-002.pdf\n │ │ └── processing/ # auto-created by the scanner while a file is in flight\n │ └── report-prep/\n │ └── ...\n ├── finishedFolders/ # outputs appear here by default (per `outputDir` placeholder)\n └── defaultWebUIConfigs/ # pre-loaded pipelines exposed in the Automate UI dropdown\n ├── invoice.json\n └── reports.json\n```\n\n### Configuration File\n\nDrop a `.json` file (any name) into each watched folder. The first `.json` the scanner finds is used as the pipeline:\n\n```json\n{\n \"name\": \"Invoice Processing\",\n \"pipeline\": [\n {\"operation\": \"/api/v1/misc/ocr-pdf\", \"parameters\": {\n \"languages\": [\"eng\"], \"ocrType\": \"skip-text\",\n \"ocrRenderType\": \"hocr\", \"deskew\": true, \"clean\": false,\n \"cleanFinal\": false, \"sidecar\": false, \"removeImagesAfter\": false}}\n ],\n \"outputDir\": \"{outputFolder}/{folderName}\",\n \"outputFileName\": \"{filename}-processed-{date}\"\n}\n```\n\nPDFs go directly in the watched folder root (NOT in an `input/` subdirectory). The scanner auto-creates a `processing/` subfolder while a file is being worked on, and writes outputs to wherever `outputDir` resolves to (typically `/pipeline/finishedFolders/...` via the `{outputFolder}` placeholder).\n\nThe watched-folder scanner runs every 60 seconds.\n\n**Learn more:** [Folder Scanning Guide](doc:configuration/storage/folderscanning)\n\n---\n\n## Best Practices\n\n### Pipeline Design\n\n1. **Test Incrementally**\n - Build pipeline one operation at a time\n - Test each step before adding the next\n - Verify output at each stage\n\n2. **Order Operations Logically**\n - Do OCR before text-based operations\n - Remove pages before processing remaining pages\n - Compress last to optimize final output\n\n3. **Use Descriptive Names**\n - Name pipelines clearly: \"Invoice-OCR-Watermark-Archive\"\n - Add descriptions in comments\n - Version your pipeline files\n\n4. **Handle Errors Gracefully**\n - Test with various file types\n - Consider edge cases (empty PDFs, locked files)\n - Monitor logs for errors\n\n### Performance Optimization\n\n1. **Minimize Operations**\n - Combine similar operations when possible\n - Remove unnecessary steps\n - Don't duplicate efforts\n\n2. **Optimize Compression**\n - Compress once at the end, not multiple times\n - Choose appropriate compression level\n - Balance quality vs. file size\n\n3. **Batch Intelligently**\n - Group similar files together\n - Process during off-peak hours\n - Monitor system resources\n\n### Maintenance\n\n1. **Version Control**\n - Keep pipeline JSONs in git repository\n - Track changes over time\n - Document modifications\n\n2. **Regular Review**\n - Audit pipelines quarterly\n - Remove unused pipelines\n - Update for new requirements\n\n3. **Monitor Performance**\n - Check processing times\n - Review error logs\n - Optimize slow operations\n\n---\n\n## Troubleshooting\n\n### Pipeline Fails to Execute\n\n**Symptoms:** Pipeline starts but doesn't complete\n\n**Common Causes:**\n- Invalid parameter values\n- Unsupported file format\n- Missing dependencies (OCR languages, fonts)\n- File permissions issues\n\n**Solutions:**\n1. Validate JSON configuration\n2. Test each operation individually\n3. Check server logs for errors\n4. Verify required dependencies installed\n\n---\n\n### `handleData` Returns Empty Response\n\n**Symptoms:** REST API call returns HTTP 200 with an empty body.\n\n**Cause:** Errors after multipart parsing (invalid operation name, missing required parameter, downstream endpoint failure) currently collapse to `200 OK` with no body. Check the server logs for the actual error.\n\n**Common reasons:**\n- Operation name used short form (e.g. `compress-pdf`) instead of full path (`/api/v1/misc/compress-pdf`)\n- Operation references an endpoint outside the allowed namespaces (only `general`, `misc`, `security`, `convert`, `filter`, `ai/tools` are permitted)\n- A required parameter was omitted (check the schema for the underlying endpoint in the [Swagger UI / API reference](#operation-and-parameter-reference))\n- The pipeline tries to call `/api/v1/pipeline/handleData` recursively\n\n---\n\n### Folder Scanning Not Working\n\n**Symptoms:** Files not processed automatically\n\n**Possible Issues:**\n- Folder permissions incorrect\n- Pipeline configuration invalid\n- Folder scanning not enabled\n\n**Solutions:**\n1. Check folder permissions (read/write access)\n2. Test pipeline manually first\n3. Check `docker logs` for errors\n4. Ensure folder scanning feature enabled\n5. The scanner runs once every 60 seconds - allow that long after dropping a file\n\n---\n\n### Operation Parameters Not Applying\n\n**Symptoms:** Pipeline runs but doesn't use specified settings\n\n**Causes:**\n- Incorrect parameter names\n- Wrong parameter data types\n- Parameters not supported in operation\n\n**Solutions:**\n1. Check parameter names against the endpoint's schema in the [Swagger UI / API reference](#operation-and-parameter-reference)\n2. Verify parameter value types (string, number, boolean)\n3. Test the same parameters by calling the endpoint directly first\n\n---\n\n### Results Not as Expected\n\n**Symptoms:** Pipeline completes but output incorrect\n\n**Debugging Steps:**\n1. Test each operation individually\n2. Check intermediate outputs\n3. Verify operation order makes sense\n4. Review parameter values\n5. Test with simpler input files\n\n---\n\n## Pipeline vs. Multi-Tool vs. Manual\n\n### Use Pipeline/Automate When:\n- Same workflow repeated frequently\n- Predictable, consistent operations\n- Automated folder processing needed\n- No manual intervention required\n- Standardizing team processes\n- Large batch processing\n- Scheduled/unattended processing\n\n### Use Multi-Tool When:\n- Workflow varies per file\n- Need visual feedback at each step\n- Experimenting with different settings\n- Manual decision points in workflow\n- One-time complex tasks\n\n### Use Individual Tools When:\n- Single, simple operation\n- Quick one-off task\n- Learning how operations work\n- No need for automation\n\n---\n\n## Security Considerations\n\n### Pipeline Files\n- **Protect JSON configs** - May contain passwords or sensitive settings\n- **Restrict folder access** - Limit who can create/modify pipelines\n- **Review before deploying** - Audit pipelines for security issues\n\n### Folder Scanning\n- **Isolate watched folders** - Don't expose to untrusted users\n- **Monitor activity** - Log all processing for audit trail\n- **Secure output folders** - Protect processed documents appropriately\n\n### Automated Processing\n- **Validate inputs** - Ensure only expected files processed\n- **Error handling** - Don't expose sensitive error messages\n- **Resource limits** - Prevent resource exhaustion attacks\n\n---\n\n## Related Documentation\n\n- **[Folder Scanning Setup](doc:configuration/storage/folderscanning)** - Detailed folder scanning guide\n- **[Multi-Tool](doc:functionality/multi-tool)** - Interactive multi-operation tool\n- **[Endpoint Customisation](doc:configuration/customisation/endpoint-or-feature-customisation)** - Operation names and IDs\n- **[API Documentation](doc:api)** - Programmatic pipeline execution\n- **[Advanced Tools](doc:functionality/advanced-tools)** - Other automation features\n\n---\n\n## Summary\n\nPipeline automation (Automate tool) transforms Stirling PDF into a workflow engine:\n\n- **Chain operations** - Combine multiple PDF tools sequentially\n- **Save workflows** - Reusable pipeline configurations\n- **Folder scanning** - Automated unattended processing\n- **REST API** - Trigger pipelines from any external system\n- **Standardization** - Consistent processing across teams\n- **Efficiency** - Minutes saved per file, hours per day\n\n**Perfect for:** Repetitive workflows, batch processing, automated document preparation, and standardized procedures.\n\nReady to automate? Create your first pipeline and transform how you process PDFs.", + "sourcePath": "docs/Configuration/Automation/Pipeline.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Automation/Pipeline.md" + }, + "configuration/automation/telegram-bot": { + "id": "configuration/automation/telegram-bot", + "title": "Telegram Bot Integration", + "description": "Process PDFs through a Telegram bot powered by Stirling PDF pipelines", + "section": "configuration/automation", + "markdown": "Stirling PDF can run a Telegram bot that accepts PDF files from chats and returns processed results using your saved [Automate / Pipeline](doc:configuration/automation/pipeline) configurations. Send a PDF to the bot, get a processed PDF back.\n\nFree on all license tiers.\n\n> **⚠️ Warning: Community feature - not recommended for Enterprise use**\n>\n> The Telegram bot integration is a **community-built feature** in **beta**. It is not built, tested, or supported by Stirling Tools and is not covered by the Server / Enterprise support SLA. **We do not recommend it for Enterprise or production-critical deployments.** Raise issues on GitHub or in the community Discord. Behaviour and config keys may change in future releases.\n\n\n---\n\n## How it works\n\n1. A user, channel, or group sends a PDF file to your Telegram bot.\n2. The bot saves the file into a watched pipeline inbox folder.\n3. Stirling PDF processes the file using the pipeline JSON in that folder (see [Folder Scanning](doc:configuration/storage/folderscanning)).\n4. The bot sends the result back to the chat.\n\nThe bot talks to Telegram outbound only - no inbound port or webhook setup required, just outbound HTTPS to `api.telegram.org`.\n\n---\n\n## Setup overview\n\n1. Create the bot in Telegram and get a bot token.\n2. Set your token and username in Stirling PDF.\n3. Drop a pipeline JSON into the inbox folder.\n4. Send a PDF to the bot.\n\n---\n\n## 1. Create the bot in Telegram\n\n1. Open [@BotFather](https://t.me/BotFather) in Telegram.\n2. Send `/newbot`.\n3. Pick a display name and a username ending in `bot`.\n4. Copy the token BotFather returns.\n\n**If you plan to add the bot to groups:** also send `/setprivacy`, select your bot, choose **Disable**. With privacy mode on (the default), the bot only sees commands and direct mentions in groups.\n\n---\n\n## 2. Configure Stirling PDF\n\nThe `telegram:` block is already present in the shipped `settings.yml` - find it and update the values (set `enabled: true`, fill in `botToken` and `botUsername`), or set the equivalent environment variables. Restart Stirling PDF after saving via file edits; the Admin UI hot-applies.\n\n### Minimal config\n\n```yaml\ntelegram:\n enabled: true\n botToken: \"your-token-from-botfather\"\n botUsername: \"your_bot_username\"\n```\n\n### Recommended config\n\n```yaml\ntelegram:\n enabled: true\n botToken: \"your-token-from-botfather\"\n botUsername: \"your_bot_username\"\n customFolderSuffix: true # one inbox per chat\n enableAllowUserIDs: true # restrict to known users\n allowUserIDs: [123456789]\n processingTimeoutSeconds: 180 # max time to wait for a result\n```\n\n### Admin UI\n\nIf you have login enabled, configure the bot from **Admin Settings → Connections → Telegram Bot**. Changes saved through the UI apply without a restart.\n\n---\n\n## 3. Drop a pipeline JSON into the inbox\n\nThe bot will not process uploads until at least one `.json` pipeline file exists in the chat's inbox folder.\n\nDefault inbox path: `/pipeline/watchedFolders/telegram/`. With `customFolderSuffix: true` (recommended), each chat gets its own subfolder named after its Telegram chat ID.\n\n### How to get a chat ID\n\nThe folder is created the first time a chat messages the bot. Easiest bootstrap:\n\n1. Send any message from the chat to the bot.\n2. Look at the new subfolder name under `/pipeline/watchedFolders/telegram/`.\n3. That subfolder name is the chat ID.\n\nOr chat with [@userinfobot](https://t.me/userinfobot) which echoes your user ID.\n\n### Build the pipeline JSON\n\nOpen the **Automate** tool, build the workflow you want, then click **Export for Folder Scanning** to download the JSON. Drop the file into the chat's inbox folder. The filename does not matter - any `.json` is picked up.\n\nSee [Pipeline Automation](doc:configuration/automation/pipeline) for details.\n\n---\n\n## 4. Use the bot\n\nIn Telegram, send the bot a PDF. The bot acknowledges, processes it via the pipeline, and sends the result back. Typical end-to-end time is 1-3 minutes (depending on what the pipeline does).\n\nOnly files with MIME type `application/pdf` are accepted.\n\n`/start` in a private chat returns a welcome message.\n\n---\n\n## Configuration reference\n\nAll options live under the top-level `telegram:` block. Environment variables use the `TELEGRAM_*` form (Spring Boot relaxed binding: dots become underscores, camelCase joins stay glued).\n\n| YAML key | Default | Purpose |\n|---|---|---|\n| `enabled` | `false` | Master toggle. |\n| `botToken` | empty | BotFather token. |\n| `botUsername` | empty | Bot username, without the `@`. |\n| `pipelineInboxFolder` | `\"telegram\"` | Subfolder name under `/pipeline/watchedFolders/`. |\n| `customFolderSuffix` | `true` | Appends the chat ID as a subdirectory so each chat has its own inbox. |\n| `enableAllowUserIDs` | `true` | Turn on user ID allowlist (for private chats). |\n| `allowUserIDs` | `[]` | Allowed user IDs. |\n| `enableAllowChannelIDs` | `true` | Turn on channel ID allowlist. |\n| `allowChannelIDs` | `[]` | Allowed channel IDs (typically negative, e.g. `-1001234567890`). |\n| `processingTimeoutSeconds` | `180` | Max wait for a pipeline result. Keep ≥ 90s. |\n| `pollingIntervalMillis` | `2000` | How often to check for results. |\n| `feedback.user.*` | all `true` | Per-message-type replies in private chats (`noValidDocument`, `errorMessage`, `errorProcessing`, `processing`). |\n| `feedback.channel.*` | all `true` | Same for channels. |\n\n---\n\n## Access control\n\n- **Private chats**: controlled by `enableAllowUserIDs` + `allowUserIDs`.\n- **Channels**: controlled by `enableAllowChannelIDs` + `allowChannelIDs`.\n- **Groups and supergroups**: **always allowed** - the allowlist does not apply. To restrict group access, either don't add the bot to groups, or use BotFather's `/setjoingroups → Disable` so it can't be invited.\n\nFor production deployments, always enable the user or channel allowlist.\n\n---\n\n## Limitations\n\n- **20 MB upload limit** (a Telegram bot API constraint, not Stirling).\n- **One JSON per chat folder** when `customFolderSuffix: true`. Create the folder by messaging the bot first, then drop the JSON in.\n- **Output is sent file-by-file** - if your pipeline emits N files, the user gets N Telegram messages, no zipping.\n\n---\n\n## Recommended deployment patterns\n\n- **Personal use**: private-chat allowlist with your own user ID. Single pipeline JSON in your chat's subfolder.\n- **Team-shared inbox**: a Telegram group, with the bot's privacy mode disabled in BotFather. One pipeline JSON for the group.\n- **Per-user pipelines**: `customFolderSuffix: true` plus a tailored pipeline JSON per chat ID.\n- **Channel posting**: add the bot as a channel admin with \"Post Messages\" permission, restrict via `allowChannelIDs`.\n\n---\n\n## Related Documentation\n\n- **[Pipeline Automation](doc:configuration/automation/pipeline)** - Build the pipeline JSONs the bot uses\n- **[Folder Scanning](doc:configuration/storage/folderscanning)** - The processing engine the bot relies on\n- **[API Documentation](doc:api)** - Trigger pipelines without Telegram", + "sourcePath": "docs/Configuration/Automation/Telegram Bot.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Automation/Telegram Bot.md" + }, + "configuration/automation/usage-monitoring": { + "id": "configuration/automation/usage-monitoring", + "title": "Usage Monitoring", + "section": "configuration/automation", + "markdown": "> **Tier**: Enterprise\n\nStirling PDF provides robust usage monitoring capabilities through its API, allowing you to track application usage patterns and performance metrics.\n\n## Non-Persistent Usage Monitoring API\n\nThe following API endpoints are available to all users to monitor usage statistics. These endpoints provide non-persistent usage data that can be queried on demand.\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /api/v1/info/status` | Application status and version information |\n| `GET /api/v1/info/requests` | Total count of POST requests for a specific endpoint (optional query parameter: `endpoint`) |\n| `GET /api/v1/info/requests/unique` | Count of unique users for POST requests for a specific endpoint |\n| `GET /api/v1/info/requests/all` | POST requests count for all endpoints |\n| `GET /api/v1/info/requests/all/unique` | Unique users count for POST requests for all endpoints |\n| `GET /api/v1/info/load` | Total count of GET requests for a specific endpoint (optional query parameter: `endpoint`) |\n| `GET /api/v1/info/load/unique` | Count of unique users for GET requests for a specific endpoint |\n| `GET /api/v1/info/load/all` | GET requests count for all endpoints |\n| `GET /api/v1/info/load/all/unique` | Unique users count for GET requests for all endpoints |\n\nAll endpoints return a JSON response with the requested statistics.\n\n## Prometheus Monitoring Configuration\n\nStirling PDF supports application metrics monitoring using Prometheus. This feature allows you to track application performance, usage patterns, and health metrics.\n\n### Prerequisites\n\n1. A valid Stirling PDF enterprise license\n2. Enterprise mode enabled in your configuration\n3. Running with additional features enabled (DISABLE_ADDITIONAL_FEATURES=false)\n\n### Configuration\n\nConfigure Prometheus monitoring using your preferred method:\n\n\n \n Configure in your `/configs/custom_settings.yml` file:\n\n ```yaml\n management:\n endpoints:\n web:\n exposure:\n include: prometheus,health,info\n endpoint:\n health:\n show-details: always\n metrics:\n export:\n prometheus:\n enabled: true\n enterprisemanagement:\n metrics:\n enabled: true\n ```\n \n \n Set the `JAVA_CUSTOM_OPTS` environment variable:\n\n ```bash\n JAVA_CUSTOM_OPTS=\"-Dmanagement.endpoints.web.exposure.include=prometheus,health,info -Dmanagement.endpoint.health.show-details=always -Dmanagement.metrics.export.prometheus.enabled=true -Denterprisemanagement.metrics.enabled=true\"\n ```\n \n \n ```bash\n docker run -d \\\n -p 8080:8080 \\\n -e JAVA_CUSTOM_OPTS=\"-Dmanagement.endpoints.web.exposure.include=prometheus,health,info -Dmanagement.endpoint.health.show-details=always -Dmanagement.metrics.export.prometheus.enabled=true -Denterprisemanagement.metrics.enabled=true\" \\\n docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n environment:\n JAVA_CUSTOM_OPTS: \"-Dmanagement.endpoints.web.exposure.include=prometheus,health,info -Dmanagement.endpoint.health.show-details=always -Dmanagement.metrics.export.prometheus.enabled=true -Denterprisemanagement.metrics.enabled=true\"\n ```\n \n\n\n**What this configures:**\n- Prometheus metrics endpoint exposure\n- Health and info endpoints for basic monitoring\n- Detailed health information\n- Prometheus metrics export\n- Enterprise metrics collection\n\n### Accessing Metrics\n\nOnce configured, Prometheus metrics are available at the following endpoint:\n\n```\nhttps://your-stirling-pdf-instance/actuator/prometheus\n```\n\nThis endpoint provides metrics in a format that can be scraped by a Prometheus server.\n\n### Configuring Prometheus Server\n\nAdd the following job configuration to your Prometheus server's configuration file (`prometheus.yml`):\n\n```yaml\nscrape_configs:\n - job_name: 'stirling-pdf'\n metrics_path: '/actuator/prometheus'\n scrape_interval: 15s\n static_configs:\n - targets: ['your-stirling-pdf-host:port']\n```\n\n### Available Metrics\n\nWith Prometheus integration enabled, Stirling PDF exposes the following types of metrics:\n- **JVM metrics**: Memory usage, garbage collection, thread utilization\n- **System metrics**: CPU usage, file descriptors\n- **Application metrics**: Request rates, processing times\n- **PDF processing metrics**: Document operations, conversion statistics", + "sourcePath": "docs/Configuration/Automation/Usage Monitoring.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Automation/Usage Monitoring.md" }, "configuration/configuration": { "id": "configuration/configuration", "title": "Configuration Guide", "description": "Configure Stirling PDF using environment variables, settings files, or in-app settings", "section": "configuration", - "markdown": "Stirling PDF can be configured in three ways, depending on your deployment and preferences.\n\n## Configuration Methods\n\n### 1. In-App Settings (Recommended)\n\nIf you have login enabled, admins can configure everything through the Settings menu in the application.\n\n**To use:**\n1. Set `SECURITY_ENABLELOGIN=true`\n2. Log in as admin\n3. Go to Settings → configure through UI\n4. Changes apply immediately, no restart needed\n\n**Best for:** Production deployments with admin users\n\n---\n\n### 2. Environment Variables\n\nConfigure via Docker environment variables or system environment variables.\n\n**To use:**\n```bash\ndocker run -d \\\n -e SECURITY_ENABLELOGIN=true \\\n -e SYSTEM_DEFAULTLOCALE=en-US \\\n stirlingtools/stirling-pdf:latest\n```\n\n**Best for:** Docker deployments, infrastructure-as-code, initial setup\n\n---\n\n### 3. Settings File (settings.yml)\n\nEdit `/configs/settings.yml` directly for advanced configuration.\n\n**To use:**\n```yaml\nsecurity:\n enableLogin: true\nsystem:\n defaultLocale: en-US\n```\n\n**Best for:** Complex configurations, when you prefer file-based config\n\n---\n\n## Common Settings\n\n### Authentication\n\n**Note:** Authentication and additional features are included by default in:\n- **Docker**: All images except ultra-lite (authentication is enabled by default)\n- **JAR**: [Stirling-PDF-with-login.jar](https://files.stirlingpdf.com/Stirling-PDF-with-login.jar) **(Recommended)**\n\nThe plain [Stirling-PDF.jar](https://files.stirlingpdf.com/Stirling-PDF.jar) does not include authentication or additional features.\n\nConfigure user login:\n\n\n \n ```yaml\n security:\n enableLogin: true\n initialLogin:\n username: admin\n password: changeme123\n ```\n \n \n ```bash\n SECURITY_ENABLELOGIN=true\n SECURITY_INITIALLOGIN_USERNAME=admin\n SECURITY_INITIALLOGIN_PASSWORD=changeme123\n ```\n \n\n\nDefault credentials: `admin` / `stirling` (change immediately after first login)\n\nFor more details, see [System and Security Configuration](doc:configuration/system-and-security).\n\n### Language & Localization\n\n\n \n ```yaml\n ui:\n languages: [] # Available languages (empty = all enabled), e.g. [\"en_US\", \"de_DE\"]\n system:\n defaultLocale: en-US # Default language for new users\n ```\n \n \n ```bash\n UI_LANGUAGES=en_US,de_DE # Restrict available languages (omit to enable all)\n SYSTEM_DEFAULTLOCALE=en-US # Default language\n ```\n \n\n\nLeaving `defaultLocale` empty (the default) auto-detects the language from the browser and falls back to `en-US` if no preference is found.\n\n**How language selection works:**\n\nStirling PDF determines the interface language using this priority order:\n\n1. **User's manual selection** (highest priority)\n - When a user clicks the language globe icon and selects a language\n - Choice is stored in browser's localStorage (persists across sessions)\n - Storage key: `i18nextLng`\n\n2. **System default locale**\n - Set via `SYSTEM_DEFAULTLOCALE` or `system.defaultLocale`\n - When configured, it overrides the browser's detected language for users who have not made a manual selection\n\n3. **Browser's language preference**\n - Automatically detected from the browser's language setting\n - Example: Firefox set to Swedish (sv-SE) shows Swedish UI when no `defaultLocale` is configured\n\n4. **Fallback** (lowest priority)\n - `en-US` is used when none of the above resolve to an available language\n\n**Example:**\n- Config: `SYSTEM_DEFAULTLOCALE=en-US`\n- Browser: Swedish (sv-SE)\n- Result: UI shows English (US) (the configured default overrides the browser preference)\n\nIf `defaultLocale` is left empty (the default), the browser-detected language is used instead. Users can always override either choice by manually selecting a language via the language globe icon.\n\n> **Tip**: Set `SYSTEM_DEFAULTLOCALE` to your organization's primary language. Users can always override it using the language selector in the top-right corner.\n\n### File Upload Limits\n\n\n \n ```yaml\n system:\n fileUploadLimit: \"500MB\" # Number (0-999) followed by KB, MB, or GB. Empty = no limit\n spring:\n servlet:\n multipart:\n max-file-size: 2000MB\n max-request-size: 2000MB\n ```\n \n \n ```bash\n SYSTEM_MAXFILESIZE=500 # Size in MB (valid range 1-999)\n SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE=2000MB\n SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE=2000MB\n ```\n \n\n\n### Memory Management\n\n```bash\nJAVA_TOOL_OPTIONS=\"-Xms512m -Xmx4g\" # Min 512MB, Max 4GB RAM\n```\n\n---\n\n## Specialized Configuration Guides\n\nFor advanced features and specific use cases, see these detailed guides:\n\n### Authentication & Security\n\n**[Single Sign-On (SSO)](doc:configuration/single-sign-on-configuration)**\n- OAuth2 (Google, GitHub, Keycloak, OIDC) - Server tier\n- SAML2 (Okta, Azure AD) - Enterprise tier\n- Complete configuration examples\n\n**[System and Security](doc:configuration/system-and-security)**\n- Server certificates\n- JWT configuration\n\n**[Fail2Ban Integration](doc:configuration/fail2ban)**\n- Protect against brute-force attacks\n- Auto-ban after failed login attempts\n\n---\n\n### Features & Customization\n\n**[UI Customization](doc:configuration/ui-customisation)**\n- Branding and logos\n- Theme customization\n- Custom styling\n\n**[Endpoint/Feature Control](doc:configuration/endpoint-or-feature-customisation)**\n- Enable/disable specific tools\n- Control feature availability by user/role\n\n**[Pipeline (Automation)](doc:configuration/pipeline)**\n- Automated workflows\n- Folder scanning\n- Batch processing\n- Multi-step operations\n\n---\n\n### Integration & Storage\n\n**[External Database](doc:configuration/external-database)**\n- PostgreSQL configuration (Pro/Enterprise)\n- Database migration\n- Backup strategies\n\n**[Google Drive File Picker](doc:configuration/google-drive-file-picker)**\n- Direct Google Drive integration\n- OAuth setup\n\n**[MCP Server](doc:advanced-configuration/mcp-server)**\n- Expose Stirling PDF tools over the Model Context Protocol\n- OAuth2 or API-key authentication\n- Operation allow/deny lists\n\n**[S3 / Object Storage](doc:configuration/file-sharing-and-storage)**\n- Store uploads and job artifacts in S3-compatible object storage\n- Shared storage for multi-node deployments\n\n**[Telegram Bot](doc:configuration/telegram-bot)**\n- Run a Telegram bot that processes PDFs sent in chat\n\n**[OCR Configuration](doc:configuration/ocr)**\n- Tesseract language packs\n- OCR optimization\n\n**[Usage Monitoring](doc:configuration/usage-monitoring)**\n- Prometheus metrics (Pro/Enterprise)\n- Application monitoring\n- Performance tracking\n\n---\n\n### Performance & Scaling\n\n**[Performance Optimization & Sizing](doc:configuration/performance-optimization)**\n- Resource sizing, JVM tuning, memory model, and scaling guidance\n\n**[Process Limits](doc:configuration/process-limits)**\n- Session limits and timeouts for external tools\n\n**[LibreOffice Parallel Processing](doc:configuration/libreoffice-parallel-processing)**\n- Configure multiple LibreOffice instances for faster document conversion\n- Local UNO server pool and remote UNO server endpoints\n\n---\n\n### Diagnostics & Support\n\n**[Diagnostics & Reporting Issues](doc:configuration/diagnostics)**\n- Built-in diagnostics tool for Docker containers\n- How to report issues via GitHub, Discord, and email\n\n---\n\n### Other Configuration\n\n**[Folder Scanning](doc:configuration/folderscanning)**\n- Watch folders for automatic processing\n\n**[Custom Signature Files](doc:configuration/sign-with-custom-files)**\n- Pre-loaded signatures for quick signing\n\n**[Extra Settings](doc:configuration/extra-settings)**\n- Logging configuration\n- Server settings (port, SSL/TLS)\n- Advanced Spring Boot settings\n\n---\n\n## Configuration Priority\n\nWhen the same setting is defined in multiple places, this is the order of precedence (highest to lowest):\n\n1. **Environment Variables**\n2. **settings.yml / In-App Settings**\n3. **Default values**\n\n---\n\n## Environment Variable Format\n\nConvert YAML paths to environment variables:\n\n```yaml\n# settings.yml\nsecurity:\n enableLogin: true\n```\n\nBecomes:\n```bash\nSECURITY_ENABLELOGIN=true\n```\n\n**Rules:**\n- Uppercase everything\n- Replace `.` with `_`\n- Nested properties become `PARENT_CHILD`\n\n---\n\n## Troubleshooting\n\n### Settings Not Applied\n\n1. Check configuration priority (env vars override settings.yml)\n2. Restart container after changing environment variables\n3. Check logs: `docker logs stirling-pdf | grep ERROR`\n4. Verify file permissions on `/configs` volume\n\n### Database Issues\n\nDefault database location: `/configs/stirling-pdf-DB-.mv.db` (the schema version is part of the filename, e.g. `/configs/stirling-pdf-DB-2.3.232.mv.db`).\n\nIf missing:\n- Ensure `/configs` volume is mounted\n- Check write permissions\n- Review startup logs\n\n---\n\n## Next Steps\n\n- **Production Deployment:** See [Production Deployment Guide](doc:server-admin-onboarding)\n- **API Usage:** See [API Documentation](doc:api)\n- **Tool Reference:** See [Functionality](doc:functionality/functionality)\n- **Troubleshooting:** See [Diagnostics & Reporting Issues](doc:configuration/diagnostics)", + "markdown": "Stirling PDF can be configured in three ways, depending on your deployment and preferences.\n\n## Configuration Methods\n\n### 1. In-App Settings (Recommended)\n\nIf you have login enabled, admins can configure everything through the Settings menu in the application.\n\n**To use:**\n1. Set `SECURITY_ENABLELOGIN=true`\n2. Log in as admin\n3. Go to Settings → configure through UI\n4. Changes apply immediately, no restart needed\n\n**Best for:** Production deployments with admin users\n\n---\n\n### 2. Environment Variables\n\nConfigure via Docker environment variables or system environment variables.\n\n**To use:**\n```bash\ndocker run -d \\\n -e SECURITY_ENABLELOGIN=true \\\n -e SYSTEM_DEFAULTLOCALE=en-US \\\n docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n```\n\n**Best for:** Docker deployments, infrastructure-as-code, initial setup\n\n---\n\n### 3. Settings File (settings.yml)\n\nEdit `/configs/settings.yml` directly for advanced configuration.\n\n**To use:**\n```yaml\nsecurity:\n enableLogin: true\nsystem:\n defaultLocale: en-US\n```\n\n**Best for:** Complex configurations, when you prefer file-based config\n\n---\n\n## Common Settings\n\n### Authentication\n\n**Note:** Authentication and additional features are included by default in:\n- **Docker**: All images except ultra-lite (authentication is enabled by default)\n- **JAR**: [Stirling-PDF-with-login.jar](https://files.stirlingpdf.com/Stirling-PDF-with-login.jar) **(Recommended)**\n\nThe plain [Stirling-PDF.jar](https://files.stirlingpdf.com/Stirling-PDF.jar) does not include authentication or additional features.\n\nConfigure user login:\n\n\n \n ```yaml\n security:\n enableLogin: true\n initialLogin:\n username: admin\n password: changeme123\n ```\n \n \n ```bash\n SECURITY_ENABLELOGIN=true\n SECURITY_INITIALLOGIN_USERNAME=admin\n SECURITY_INITIALLOGIN_PASSWORD=changeme123\n ```\n \n\n\nDefault credentials: `admin` / `stirling` (change immediately after first login)\n\nFor more details, see [System and Security Configuration](doc:configuration/security/system-and-security).\n\n### Language & Localization\n\n\n \n ```yaml\n ui:\n languages: [] # Available languages (empty = all enabled), e.g. [\"en_US\", \"de_DE\"]\n system:\n defaultLocale: en-US # Default language for new users\n ```\n \n \n ```bash\n UI_LANGUAGES=en_US,de_DE # Restrict available languages (omit to enable all)\n SYSTEM_DEFAULTLOCALE=en-US # Default language\n ```\n \n\n\nLeaving `defaultLocale` empty (the default) auto-detects the language from the browser and falls back to `en-US` if no preference is found.\n\n**How language selection works:**\n\nStirling PDF determines the interface language using this priority order:\n\n1. **User's manual selection** (highest priority)\n - When a user clicks the language globe icon and selects a language\n - Choice is stored in browser's localStorage (persists across sessions)\n - Storage key: `i18nextLng`\n\n2. **System default locale**\n - Set via `SYSTEM_DEFAULTLOCALE` or `system.defaultLocale`\n - When configured, it overrides the browser's detected language for users who have not made a manual selection\n\n3. **Browser's language preference**\n - Automatically detected from the browser's language setting\n - Example: Firefox set to Swedish (sv-SE) shows Swedish UI when no `defaultLocale` is configured\n\n4. **Fallback** (lowest priority)\n - `en-US` is used when none of the above resolve to an available language\n\n**Example:**\n- Config: `SYSTEM_DEFAULTLOCALE=en-US`\n- Browser: Swedish (sv-SE)\n- Result: UI shows English (US) (the configured default overrides the browser preference)\n\nIf `defaultLocale` is left empty (the default), the browser-detected language is used instead. Users can always override either choice by manually selecting a language via the language globe icon.\n\n> **Tip**: Set `SYSTEM_DEFAULTLOCALE` to your organization's primary language. Users can always override it using the language selector in the top-right corner.\n\n### File Upload Limits\n\n\n \n ```yaml\n system:\n fileUploadLimit: \"500MB\" # Number (0-999) followed by KB, MB, or GB. Empty = no limit\n spring:\n servlet:\n multipart:\n max-file-size: 2000MB\n max-request-size: 2000MB\n ```\n \n \n ```bash\n SYSTEM_MAXFILESIZE=500 # Size in MB (valid range 1-999)\n SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE=2000MB\n SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE=2000MB\n ```\n \n\n\n### Memory Management\n\n```bash\nJAVA_TOOL_OPTIONS=\"-Xms512m -Xmx4g\" # Min 512MB, Max 4GB RAM\n```\n\n---\n\n## Specialized Configuration Guides\n\nFor advanced features and specific use cases, see these detailed guides:\n\n### Authentication & Security\n\n**[Single Sign-On (SSO)](doc:configuration/security/single-sign-on-configuration)**\n- OAuth2 (Google, GitHub, Keycloak, OIDC) - Server tier\n- SAML2 (Okta, Azure AD) - Enterprise tier\n- Complete configuration examples\n\n**[System and Security](doc:configuration/security/system-and-security)**\n- Server certificates\n- JWT configuration\n\n**[Fail2Ban Integration](doc:configuration/security/fail2ban)**\n- Protect against brute-force attacks\n- Auto-ban after failed login attempts\n\n---\n\n### Features & Customization\n\n**[UI Customization](doc:configuration/customisation/ui-customisation)**\n- Branding and logos\n- Theme customization\n- Custom styling\n\n**[Endpoint/Feature Control](doc:configuration/customisation/endpoint-or-feature-customisation)**\n- Enable/disable specific tools\n- Control feature availability by user/role\n\n**[Pipeline (Automation)](doc:configuration/automation/pipeline)**\n- Automated workflows\n- Folder scanning\n- Batch processing\n- Multi-step operations\n\n---\n\n### Integration & Storage\n\n**[External Database](doc:configuration/storage/external-database)**\n- PostgreSQL configuration (Pro/Enterprise)\n- Database migration\n- Backup strategies\n\n**[Google Drive File Picker](doc:configuration/storage/google-drive-file-picker)**\n- Direct Google Drive integration\n- OAuth setup\n\n**[MCP Server](doc:configuration/automation/mcp-server)**\n- Expose Stirling PDF tools over the Model Context Protocol\n- OAuth2 or API-key authentication\n- Operation allow/deny lists\n\n**[S3 / Object Storage](doc:configuration/storage/file-sharing-and-storage)**\n- Store uploads and job artifacts in S3-compatible object storage\n- Shared storage for multi-node deployments\n\n**[Telegram Bot](doc:configuration/automation/telegram-bot)**\n- Run a Telegram bot that processes PDFs sent in chat\n\n**[OCR Configuration](doc:configuration/operations/ocr)**\n- Tesseract language packs\n- OCR optimization\n\n**[Usage Monitoring](doc:configuration/automation/usage-monitoring)**\n- Prometheus metrics (Pro/Enterprise)\n- Application monitoring\n- Performance tracking\n\n---\n\n### Performance & Scaling\n\n**[Performance Optimization & Sizing](doc:configuration/operations/performance-optimization)**\n- Resource sizing, JVM tuning, memory model, and scaling guidance\n\n**[Process Limits](doc:configuration/operations/process-limits)**\n- Session limits and timeouts for external tools\n\n**[LibreOffice Parallel Processing](doc:configuration/operations/libreoffice-parallel-processing)**\n- Configure multiple LibreOffice instances for faster document conversion\n- Local UNO server pool and remote UNO server endpoints\n\n---\n\n### Diagnostics & Support\n\n**[Diagnostics & Reporting Issues](doc:configuration/operations/diagnostics)**\n- Built-in diagnostics tool for Docker containers\n- How to report issues via GitHub, Discord, and email\n\n---\n\n### Other Configuration\n\n**[Folder Scanning](doc:configuration/storage/folderscanning)**\n- Watch folders for automatic processing\n\n**[Custom Signature Files](doc:configuration/security/sign-with-custom-files)**\n- Pre-loaded signatures for quick signing\n\n**[Extra Settings](doc:configuration/customisation/extra-settings)**\n- Logging configuration\n- Server settings (port, SSL/TLS)\n- Advanced Spring Boot settings\n\n---\n\n## Configuration Priority\n\nWhen the same setting is defined in multiple places, this is the order of precedence (highest to lowest):\n\n1. **Environment Variables**\n2. **settings.yml / In-App Settings**\n3. **Default values**\n\n---\n\n## Environment Variable Format\n\nConvert YAML paths to environment variables:\n\n```yaml\n# settings.yml\nsecurity:\n enableLogin: true\n```\n\nBecomes:\n```bash\nSECURITY_ENABLELOGIN=true\n```\n\n**Rules:**\n- Uppercase everything\n- Replace `.` with `_`\n- Nested properties become `PARENT_CHILD`\n\n---\n\n## Troubleshooting\n\n### Settings Not Applied\n\n1. Check configuration priority (env vars override settings.yml)\n2. Restart container after changing environment variables\n3. Check logs: `docker logs stirling-pdf | grep ERROR`\n4. Verify file permissions on `/configs` volume\n\n### Database Issues\n\nDefault database location: `/configs/stirling-pdf-DB-.mv.db` (the schema version is part of the filename, e.g. `/configs/stirling-pdf-DB-2.3.232.mv.db`).\n\nIf missing:\n- Ensure `/configs` volume is mounted\n- Check write permissions\n- Review startup logs\n\n---\n\n## Next Steps\n\n- **Production Deployment:** See [Production Deployment Guide](doc:server-admin-onboarding)\n- **API Usage:** See [API Documentation](doc:api)\n- **Tool Reference:** See [Functionality](doc:functionality/functionality)\n- **Troubleshooting:** See [Diagnostics & Reporting Issues](doc:configuration/operations/diagnostics)", "sourcePath": "docs/Configuration/Configuration.md", "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Configuration.md" }, - "configuration/database": { - "id": "configuration/database", - "title": "Database Backups", - "section": "configuration", - "markdown": "> **Tier**: Server\n\n## Functionality Overview\n\nThe newly introduced feature enhances the application with robust database backup and import capabilities. This feature is designed to ensure data integrity and provide a straightforward way to manage database backups. Here's how it works:\n\n1. Automatic Backup Creation\n - The system automatically creates a database backup every day at midnight. This ensures that there is always a recent backup available, minimizing the risk of data loss.\n2. Manual Backup Export\n - Admin actions that modify the user database trigger a manual export of the database. This keeps the backup up-to-date with the latest changes and provides an extra layer of data security.\n3. Importing Database Backups\n - Admin users can import a database backup either via the web interface or API endpoints. This allows for easy restoration of the database to a previous state in case of data corruption or other issues.\n - The import process ensures that the database structure and data are correctly restored, maintaining the integrity of the application.\n4. Managing Backup Files\n - Admins can view a list of all existing backup files, along with their creation dates and sizes. This helps in managing storage and identifying the most recent or relevant backups.\n - Backup files can be downloaded for offline storage or transferred to other environments, providing flexibility in database management.\n - Unnecessary backup files can be deleted through the interface to free up storage space and maintain an organized backup directory.\n\n## User Interface\n\n### Web Interface\n\n1. Upload SQL files to import database backups.\n2. View details of existing backups, such as file names, creation dates, and sizes.\n3. Download backup files for offline storage.\n4. Delete outdated or unnecessary backup files.\n\n### API Endpoints\n\n1. Import database backups by uploading SQL files.\n2. Download backup files.\n3. Delete backup files.\n\nThis new functionality streamlines database management, ensuring that backups are always available and easy to manage, thus improving the reliability and resilience of the application.", - "sourcePath": "docs/Configuration/DATABASE.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/DATABASE.md" - }, - "configuration/diagnostics": { - "id": "configuration/diagnostics", - "title": "Diagnostics & Reporting Issues", - "description": "Use the built-in diagnostics tool and learn how to report issues effectively", - "section": "configuration", - "markdown": "Stirling PDF includes a built-in diagnostics tool inside Docker containers that collects logs, configuration, system information, and application metrics into a single archive. This is the fastest way to gather the information needed when troubleshooting or reporting issues.\n\n---\n\n## Running the Diagnostics Tool\n\nOpen an interactive shell inside the running container and invoke the tool:\n\n```bash\ndocker exec -it diag\n```\n\nThe following aliases all work identically: `diag`, `debug`, `diagnostic`, `diagnostics`, `stirling-diagnostics`.\n\n> **⚠️ Caution: Interactive Terminal Required**\n>\n> The diagnostics tool requires an interactive terminal (`-it` flag). It will not run in non-interactive or headless sessions.\n\n\n---\n\n## Collection Modes\n\nWhen you run the tool, you'll be prompted to choose a collection mode.\n\n### Auto Mode (Recommended)\n\nSelect option **1** when prompted. Auto mode collects:\n\n- Application logs from the last 24 hours\n- Configuration files from `/configs`\n- System information (OS, CPU, memory, disk, Java version)\n- Application metrics and health endpoints\n\nThis is sufficient for most issue reports.\n\n### Custom Mode\n\nSelect option **2** for granular control over what gets collected:\n\n| Prompt | Default | What It Collects |\n|---|---|---|\n| Output directory | `/configs` | Where to save the archive |\n| Days of logs | 1 | How many days of logs to include |\n| Include /configs | Yes | Configuration files |\n| Include /customFiles | No | Custom files (excluding PDFs and images) |\n| Include /pipeline | No | Pipeline working files (excluding PDFs) |\n| Include /tmp/stirling-pdf | No | Temporary processing files |\n| Include system information | Yes | OS, CPU, RAM, disk, Java/Python versions |\n| Include environment variables | No | Full environment dump |\n| Fetch metrics endpoints | Yes | Application status, health, and load data |\n| Include UI data endpoints | No | Sign, pipeline, and OCR endpoint data |\n| Redact sensitive information | Yes | Apply redaction filters (see below) |\n\n### Redaction Options\n\nWhen redaction is enabled, you can selectively mask:\n\n- **Secrets/tokens/passwords** - Redacts Authorization headers, API keys, passwords, and similar credentials\n- **URL hosts/domains** - Masks hostnames in URLs\n- **Email addresses** - Replaces email addresses with `[REDACTED_EMAIL]`\n- **Host/Domain/Server fields** - Masks values in host-related configuration fields\n\n> **⚠️ Caution**\n>\n> Always enable redaction if you plan to share the diagnostics bundle publicly (for example, in a GitHub issue). However, redaction is not perfect and may miss some sensitive values - always review the output manually before sharing publicly. You can disable redaction for private support channels if full detail is needed.\n\n\n---\n\n## What Gets Collected\n\nThe diagnostics bundle is a `.tar.gz` archive saved to the output directory (default: `/configs`). It contains:\n\n```\nstirling-diagnostics-YYYYMMDD-HHMMSS.tar.gz\n├── summary.txt # Collection metadata and settings\n├── bundle/\n│ ├── logs/ # Application log files\n│ ├── configs/ # Configuration files (settings.yml, etc.)\n│ ├── system/ # System information\n│ │ ├── uname.txt # Kernel version\n│ │ ├── os-release # OS distribution info\n│ │ ├── meminfo.txt # Memory details\n│ │ ├── cpuinfo.txt # CPU details\n│ │ ├── df.txt # Disk usage\n│ │ ├── free.txt # Memory summary\n│ │ ├── ps.txt # Running processes\n│ │ ├── java-version.txt # Java runtime version\n│ │ └── python-version.txt # Python version\n│ ├── metrics/ # Application metrics\n│ │ ├── api/v1/info/status.json\n│ │ ├── api/v1/info/uptime.json\n│ │ ├── api/v1/info/health.json\n│ │ ├── api/v1/info/requests.json\n│ │ ├── api/v1/info/load.json\n│ │ ├── actuator/health.json\n│ │ └── actuator/prometheus.txt\n│ ├── env/ # Environment variables (if requested)\n│ └── tree/ # Directory listings\n│ ├── logs.txt\n│ ├── configs.txt\n│ ├── customFiles.txt\n│ ├── pipeline.txt\n│ ├── tessdata.txt # Installed OCR language packs\n│ └── tessdata-mount.txt\n```\n\nPDFs, images, and compressed archives are always excluded from collection.\n\n### Retrieving the Bundle\n\nAfter the tool finishes, copy the archive out of the container:\n\n```bash\ndocker cp :/configs/stirling-diagnostics-*.tar.gz ./\n```\n\n---\n\n## AOT Diagnostics\n\nIf you are running with AOT (Ahead-of-Time) compilation enabled (`STIRLING_AOT_ENABLE=true`), an additional diagnostics tool is available:\n\n```bash\ndocker exec -it aot-diag\n```\n\nThis tool diagnoses AOT cache generation failures, particularly on ARM64/aarch64 platforms. It checks cache integrity, JVM compatibility, and can run smoke tests.\n\nAliases: `aot-diag`, `aot-diagnostics`\n\n---\n\n## How to Report Issues\n\nWhen you encounter a problem with Stirling PDF, choose the right channel depending on the nature of your issue.\n\n### GitHub Issues - Bug Reports & Feature Requests\n\nFor reproducible bugs and feature requests, open an issue at:\n**https://github.com/Stirling-Tools/Stirling-PDF/issues**\n\nThe repository includes issue templates for bug reports and feature requests that will guide you through providing the right information.\n\nWhen submitting a bug report, include as much detail as possible: the diagnostics bundle (run `diag` in your container first), steps to reproduce the issue, expected vs. actual behavior, your deployment method (Docker, bare metal, Kubernetes), Stirling PDF version (visible in the UI footer or in `summary.txt` from the diagnostics bundle), and any commands, API requests, or actions you were performing when the issue occurred. The more context you provide, the faster it can be resolved.\n\n### Discord Community - Questions & Discussion\n\nFor quick questions, troubleshooting help, and community discussion:\n**https://discord.gg/HYmhKj45pU**\n\nDiscord is the best place for configuration help, setup questions, sharing workarounds with other users, general discussion about features and usage, and getting faster informal feedback before filing a formal issue. It's also great for following up on GitHub issues and having deeper conversations with the community.\n\n### Email Support\n\nFor enterprise customers and licensing inquiries:\n**support@stirlingpdf.com**\n\nFor security vulnerabilities:\n**security@stirlingpdf.com** or use the [GitHub Security Advisory](https://github.com/Stirling-Tools/Stirling-PDF/security) process.", - "sourcePath": "docs/Configuration/Diagnostics.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Diagnostics.md" - }, - "configuration/endpoint-or-feature-customisation": { - "id": "configuration/endpoint-or-feature-customisation", + "configuration/customisation/endpoint-or-feature-customisation": { + "id": "configuration/customisation/endpoint-or-feature-customisation", "title": "Endpoints Customisation", - "section": "configuration", - "markdown": "You can selectively disable and remove endpoints and functionalities from Stirling PDF as per your requirements.\nThere are many use-cases for this such as\n- Avoid confusion for users for functionality you/your business don't use.\n- Running a reduced version of Stirling PDF that doesn't have the necessary server power to support the more advanced features.\n- Cleanup interface for features you don't use\n\nYou have two ways to disable endpoints:\n\n1. **Environment Variables** (`ENDPOINTS_TOREMOVE` and `ENDPOINTS_GROUPSTOREMOVE`):\n - Example: `ENDPOINTS_TOREMOVE=merge-pdfs,remove-pages` disables merge and remove page tools\n - Example: `ENDPOINTS_GROUPSTOREMOVE=LibreOffice` disables all LibreOffice-dependent tools\n\n2. **Settings File** (`settings.yml` under `endpoints.toRemove` and `endpoints.groupsToRemove`):\n - Example: `toRemove: [merge-pdfs, remove-pages]`\n - Example: `groupsToRemove: [LibreOffice]`\n\n## Available Endpoint Groups\n\nYou can disable entire groups of related endpoints using `ENDPOINTS_GROUPSTOREMOVE`:\n\n| Group Name | What It Disables | Example |\n|------------|------------------|---------|\n| `LibreOffice` | All office document conversions (DOCX, XLSX, PPTX to/from PDF) | `ENDPOINTS_GROUPSTOREMOVE=LibreOffice` |\n| `Python` | Python-backed features (scan extraction and some file/HTML/URL conversions) | `ENDPOINTS_GROUPSTOREMOVE=Python` |\n| `OpenCV` | Advanced image processing operations | `ENDPOINTS_GROUPSTOREMOVE=OpenCV` |\n| `OCRmyPDF` | OCR (Optical Character Recognition) features | `ENDPOINTS_GROUPSTOREMOVE=OCRmyPDF` |\n| `Weasyprint` | HTML to PDF conversion | `ENDPOINTS_GROUPSTOREMOVE=Weasyprint` |\n| `Calibre` | E-book format conversions | `ENDPOINTS_GROUPSTOREMOVE=Calibre` |\n| `qpdf` | Various PDF operations powered by QPDF | `ENDPOINTS_GROUPSTOREMOVE=qpdf` |\n| `Ghostscript` | Compression, repair and related operations powered by Ghostscript | `ENDPOINTS_GROUPSTOREMOVE=Ghostscript` |\n| `Automation` | Automation/pipeline endpoints (`handleData`, `automate`, `pipeline`) | `ENDPOINTS_GROUPSTOREMOVE=Automation` |\n| `DeveloperTools` | Developer tools such as Show JavaScript | `ENDPOINTS_GROUPSTOREMOVE=DeveloperTools` |\n| `DeveloperDocs` | In-app developer doc links (API docs, folder scanning, SSO, air-gapped) | `ENDPOINTS_GROUPSTOREMOVE=DeveloperDocs` |\n\n**Example - Disable multiple groups:**\n```bash\nENDPOINTS_GROUPSTOREMOVE=LibreOffice,Calibre,Weasyprint\n```\n\n## Usage Examples\n\n### Environment Variables\n\n**Disable specific tools:**\n```bash\n# Docker Run\ndocker run -e ENDPOINTS_TOREMOVE=sign,add-watermark,add-stamp stirlingtools/stirling-pdf:latest\n\n# Docker Compose\nenvironment:\n - ENDPOINTS_TOREMOVE=sign,add-watermark,add-stamp\n```\n\n**Disable entire groups:**\n```bash\n# Disable all office conversions and OCR\nENDPOINTS_GROUPSTOREMOVE=LibreOffice,OCRmyPDF\n```\n\n**Combine both methods:**\n```bash\n# Disable groups AND specific tools\nENDPOINTS_GROUPSTOREMOVE=LibreOffice,Calibre\nENDPOINTS_TOREMOVE=sign,compare,multi-tool\n```\n\n### Settings File (settings.yml)\n\nIf you're editing `settings.yml` directly, use the kebab-case endpoint IDs:\n\n**Disable specific tools:**\n```yaml\nendpoints:\n toRemove:\n - sign\n - add-watermark\n - add-stamp\n - compare\n - merge-pdfs\n```\n\n**Disable entire groups:**\n```yaml\nendpoints:\n groupsToRemove:\n - LibreOffice\n - OCRmyPDF\n```\n\n**Combine both methods:**\n```yaml\nendpoints:\n toRemove:\n - sign\n - compare\n - multi-tool\n - merge-pdfs\n groupsToRemove:\n - LibreOffice\n - Calibre\n```\n\n## Complete Endpoint Reference for settings.yml\n\nUse these exact kebab-case IDs with `endpoints.toRemove` in settings.yml.\n\n### Page Operations\n- `merge-pdfs` - Merge PDFs\n- `split-pages` - Split PDFs\n- `extract-pages` - Extract Pages\n- `remove-pages` - Remove Pages\n- `rearrange-pages` - Rearrange Pages\n- `rotate-pdf` - Rotate PDFs\n- `crop` - Crop Pages\n- `scale-pages` - Scale Pages\n- `add-page-numbers` - Add Page Numbers\n- `pdf-to-single-page` - PDF to Single Page\n- `multi-page-layout` - Multi-Page Layout\n- `booklet-imposition` - Booklet Imposition\n- `overlay-pdf` - Overlay PDFs\n- `split-pdf-by-sections` - Split by Sections\n- `split-pdf-by-chapters` - Split by Chapters\n- `auto-split-pdf` - Auto Split PDF\n- `split-by-size-or-count` - Split by Size/Count\n- `add-attachments` - Add Attachments\n\n### Conversion\n- `pdf-to-img` - PDF to Image\n- `img-to-pdf` - Image to PDF\n- `file-to-pdf` - File to PDF\n- `pdf-to-word` - PDF to Word\n- `pdf-to-presentation` - PDF to Presentation\n- `pdf-to-text` - PDF to Text\n- `pdf-to-html` - PDF to HTML\n- `pdf-to-xml` - PDF to XML\n- `pdf-to-markdown` - PDF to Markdown\n- `pdf-to-csv` - PDF to CSV\n- `pdf-to-epub` - PDF to EPUB\n- `pdf-to-vector` - PDF to Vector\n- `pdf-to-json` - PDF to JSON\n- `pdf-to-rtf` - PDF to RTF\n- `pdf-to-cbz` - PDF to CBZ\n- `pdf-to-cbr` - PDF to CBR\n- `pdf-to-pdfa` - PDF to PDF/A\n- `html-to-pdf` - HTML to PDF\n- `url-to-pdf` - URL to PDF\n- `markdown-to-pdf` - Markdown to PDF\n- `eml-to-pdf` - Email to PDF\n- `cbz-to-pdf` - CBZ to PDF\n- `json-to-pdf` - JSON to PDF\n- `vector-to-pdf` - Vector to PDF\n\n### Security & Signing\n- `add-password` - Add Password Protection\n- `remove-password` - Remove Password\n- `change-permissions` - Change Permissions\n- `add-watermark` - Add Watermark\n- `add-stamp` - Add Stamp\n- `sanitize-pdf` - Sanitize PDF\n- `flatten` - Flatten Form Fields\n- `unlock-pdf-forms` - Unlock PDF Forms\n- `cert-sign` - Certificate Sign\n- `sign` - Draw/Text/Image Signature\n- `timestamp-pdf` - Add Trusted Timestamp\n- `remove-cert-sign` - Remove Certificate Signature\n- `validate-signature` - Validate Signature\n- `verify-pdf` - Verify PDF\n- `redact` - Redact Information\n- `auto-redact` - Auto Redact\n\n### Content Extraction & Removal\n- `extract-images` - Extract Images\n- `extract-image-scans` - Extract Image Scans\n- `remove-image-pdf` - Remove Images\n- `remove-annotations` - Remove Annotations\n- `remove-blanks` - Remove Blank Pages\n- `ocr-pdf` - OCR\n\n### Document Editing & Analysis\n- `text-editor-pdf` - Text Editor\n- `edit-table-of-contents` - Edit Table of Contents\n- `update-metadata` - Change Metadata\n- `get-info-on-pdf` - Get PDF Info\n- `compare` - Compare PDFs\n- `adjust-contrast` - Adjust Contrast\n- `replace-invert-pdf` - Replace/Invert Colors\n- `scanner-effect` - Scanner Effect\n- `repair` - Repair PDF\n- `add-image` - Add Image to PDF\n\n### Form Fields\n- `fields` - Form Fields\n- `fill` - Fill Form Fields\n- `modify-fields` - Modify Form Fields\n- `delete-fields` - Delete Form Fields\n\n### Multi-Tool & Automation\n- `multi-tool` - Multi-Tool Workbench\n- `compare` - Compare PDFs\n- `compress-pdf` - Compress PDFs\n- `automate` - Automation/Pipeline\n- `pipeline` - Pipeline\n- `auto-rename` - Auto Rename\n\n### Viewing & Display\n- `view-pdf` - PDF Viewer\n- `show-javascript` - Show JavaScript in PDF\n\n### Developer Tools\n- `dev-api-docs` - API Documentation\n- `dev-folder-scanning-docs` - Folder Scanning Guide\n- `dev-sso-guide-docs` - SSO Guide\n- `dev-airgapped-docs` - Air-gapped Setup Guide\n\n### Internal\n- `handleData` - Handle Data\n\n## Notes\n\n- Tool IDs are case-sensitive (use exact names from the reference above)\n- Group IDs are also case-sensitive - note the lowercase `qpdf`\n- Disabling a tool removes it completely from the UI and API\n- Some tools may depend on others - test your configuration\n- Changes require container restart to take effect\n- The [MCP server](doc:advanced-configuration/mcp-server) applies its own allow/deny filtering on top of these removals, so an endpoint can be enabled here yet still be hidden from MCP clients", - "sourcePath": "docs/Configuration/Endpoint or Feature Customisation.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Endpoint or Feature Customisation.md" + "section": "configuration/customisation", + "markdown": "You can selectively disable and remove endpoints and functionalities from Stirling PDF as per your requirements.\nThere are many use-cases for this such as\n- Avoid confusion for users for functionality you/your business don't use.\n- Running a reduced version of Stirling PDF that doesn't have the necessary server power to support the more advanced features.\n- Cleanup interface for features you don't use\n\nYou have two ways to disable endpoints:\n\n1. **Environment Variables** (`ENDPOINTS_TOREMOVE` and `ENDPOINTS_GROUPSTOREMOVE`):\n - Example: `ENDPOINTS_TOREMOVE=merge-pdfs,remove-pages` disables merge and remove page tools\n - Example: `ENDPOINTS_GROUPSTOREMOVE=LibreOffice` disables all LibreOffice-dependent tools\n\n2. **Settings File** (`settings.yml` under `endpoints.toRemove` and `endpoints.groupsToRemove`):\n - Example: `toRemove: [merge-pdfs, remove-pages]`\n - Example: `groupsToRemove: [LibreOffice]`\n\n## Available Endpoint Groups\n\nYou can disable entire groups of related endpoints using `ENDPOINTS_GROUPSTOREMOVE`:\n\n| Group Name | What It Disables | Example |\n|------------|------------------|---------|\n| `LibreOffice` | All office document conversions (DOCX, XLSX, PPTX to/from PDF) | `ENDPOINTS_GROUPSTOREMOVE=LibreOffice` |\n| `Python` | Python-backed features (scan extraction and some file/HTML/URL conversions) | `ENDPOINTS_GROUPSTOREMOVE=Python` |\n| `OpenCV` | Advanced image processing operations | `ENDPOINTS_GROUPSTOREMOVE=OpenCV` |\n| `OCRmyPDF` | OCR (Optical Character Recognition) features | `ENDPOINTS_GROUPSTOREMOVE=OCRmyPDF` |\n| `Weasyprint` | HTML to PDF conversion | `ENDPOINTS_GROUPSTOREMOVE=Weasyprint` |\n| `Calibre` | E-book format conversions | `ENDPOINTS_GROUPSTOREMOVE=Calibre` |\n| `qpdf` | Various PDF operations powered by QPDF | `ENDPOINTS_GROUPSTOREMOVE=qpdf` |\n| `Ghostscript` | Compression, repair and related operations powered by Ghostscript | `ENDPOINTS_GROUPSTOREMOVE=Ghostscript` |\n| `Automation` | Automation/pipeline endpoints (`handleData`, `automate`, `pipeline`) | `ENDPOINTS_GROUPSTOREMOVE=Automation` |\n| `DeveloperTools` | Developer tools such as Show JavaScript | `ENDPOINTS_GROUPSTOREMOVE=DeveloperTools` |\n| `DeveloperDocs` | In-app developer doc links (API docs, folder scanning, SSO, air-gapped) | `ENDPOINTS_GROUPSTOREMOVE=DeveloperDocs` |\n\n**Example - Disable multiple groups:**\n```bash\nENDPOINTS_GROUPSTOREMOVE=LibreOffice,Calibre,Weasyprint\n```\n\n## Usage Examples\n\n### Environment Variables\n\n**Disable specific tools:**\n```bash\n# Docker Run\ndocker run -e ENDPOINTS_TOREMOVE=sign,add-watermark,add-stamp docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n\n# Docker Compose\nenvironment:\n - ENDPOINTS_TOREMOVE=sign,add-watermark,add-stamp\n```\n\n**Disable entire groups:**\n```bash\n# Disable all office conversions and OCR\nENDPOINTS_GROUPSTOREMOVE=LibreOffice,OCRmyPDF\n```\n\n**Combine both methods:**\n```bash\n# Disable groups AND specific tools\nENDPOINTS_GROUPSTOREMOVE=LibreOffice,Calibre\nENDPOINTS_TOREMOVE=sign,compare,multi-tool\n```\n\n### Settings File (settings.yml)\n\nIf you're editing `settings.yml` directly, use the kebab-case endpoint IDs:\n\n**Disable specific tools:**\n```yaml\nendpoints:\n toRemove:\n - sign\n - add-watermark\n - add-stamp\n - compare\n - merge-pdfs\n```\n\n**Disable entire groups:**\n```yaml\nendpoints:\n groupsToRemove:\n - LibreOffice\n - OCRmyPDF\n```\n\n**Combine both methods:**\n```yaml\nendpoints:\n toRemove:\n - sign\n - compare\n - multi-tool\n - merge-pdfs\n groupsToRemove:\n - LibreOffice\n - Calibre\n```\n\n## Complete Endpoint Reference for settings.yml\n\nUse these exact kebab-case IDs with `endpoints.toRemove` in settings.yml.\n\n### Page Operations\n- `merge-pdfs` - Merge PDFs\n- `split-pages` - Split PDFs\n- `extract-pages` - Extract Pages\n- `remove-pages` - Remove Pages\n- `rearrange-pages` - Rearrange Pages\n- `rotate-pdf` - Rotate PDFs\n- `crop` - Crop Pages\n- `scale-pages` - Scale Pages\n- `add-page-numbers` - Add Page Numbers\n- `pdf-to-single-page` - PDF to Single Page\n- `multi-page-layout` - Multi-Page Layout\n- `booklet-imposition` - Booklet Imposition\n- `overlay-pdf` - Overlay PDFs\n- `split-pdf-by-sections` - Split by Sections\n- `split-pdf-by-chapters` - Split by Chapters\n- `auto-split-pdf` - Auto Split PDF\n- `split-by-size-or-count` - Split by Size/Count\n- `add-attachments` - Add Attachments\n\n### Conversion\n- `pdf-to-img` - PDF to Image\n- `img-to-pdf` - Image to PDF\n- `file-to-pdf` - File to PDF\n- `pdf-to-word` - PDF to Word\n- `pdf-to-presentation` - PDF to Presentation\n- `pdf-to-text` - PDF to Text\n- `pdf-to-html` - PDF to HTML\n- `pdf-to-xml` - PDF to XML\n- `pdf-to-markdown` - PDF to Markdown\n- `pdf-to-csv` - PDF to CSV\n- `pdf-to-epub` - PDF to EPUB\n- `pdf-to-vector` - PDF to Vector\n- `pdf-to-json` - PDF to JSON\n- `pdf-to-rtf` - PDF to RTF\n- `pdf-to-cbz` - PDF to CBZ\n- `pdf-to-cbr` - PDF to CBR\n- `pdf-to-pdfa` - PDF to PDF/A\n- `html-to-pdf` - HTML to PDF\n- `url-to-pdf` - URL to PDF\n- `markdown-to-pdf` - Markdown to PDF\n- `eml-to-pdf` - Email to PDF\n- `cbz-to-pdf` - CBZ to PDF\n- `json-to-pdf` - JSON to PDF\n- `vector-to-pdf` - Vector to PDF\n\n### Security & Signing\n- `add-password` - Add Password Protection\n- `remove-password` - Remove Password\n- `change-permissions` - Change Permissions\n- `add-watermark` - Add Watermark\n- `add-stamp` - Add Stamp\n- `sanitize-pdf` - Sanitize PDF\n- `flatten` - Flatten Form Fields\n- `unlock-pdf-forms` - Unlock PDF Forms\n- `cert-sign` - Certificate Sign\n- `sign` - Draw/Text/Image Signature\n- `timestamp-pdf` - Add Trusted Timestamp\n- `remove-cert-sign` - Remove Certificate Signature\n- `validate-signature` - Validate Signature\n- `verify-pdf` - Verify PDF\n- `redact` - Redact Information\n- `auto-redact` - Auto Redact\n\n### Content Extraction & Removal\n- `extract-images` - Extract Images\n- `extract-image-scans` - Extract Image Scans\n- `remove-image-pdf` - Remove Images\n- `remove-annotations` - Remove Annotations\n- `remove-blanks` - Remove Blank Pages\n- `ocr-pdf` - OCR\n\n### Document Editing & Analysis\n- `text-editor-pdf` - Text Editor\n- `edit-table-of-contents` - Edit Table of Contents\n- `update-metadata` - Change Metadata\n- `get-info-on-pdf` - Get PDF Info\n- `compare` - Compare PDFs\n- `adjust-contrast` - Adjust Contrast\n- `replace-invert-pdf` - Replace/Invert Colors\n- `scanner-effect` - Scanner Effect\n- `repair` - Repair PDF\n- `add-image` - Add Image to PDF\n\n### Form Fields\n- `fields` - Form Fields\n- `fill` - Fill Form Fields\n- `modify-fields` - Modify Form Fields\n- `delete-fields` - Delete Form Fields\n\n### Multi-Tool & Automation\n- `multi-tool` - Multi-Tool Workbench\n- `compare` - Compare PDFs\n- `compress-pdf` - Compress PDFs\n- `automate` - Automation/Pipeline\n- `pipeline` - Pipeline\n- `auto-rename` - Auto Rename\n\n### Viewing & Display\n- `view-pdf` - PDF Viewer\n- `show-javascript` - Show JavaScript in PDF\n\n### Developer Tools\n- `dev-api-docs` - API Documentation\n- `dev-folder-scanning-docs` - Folder Scanning Guide\n- `dev-sso-guide-docs` - SSO Guide\n- `dev-airgapped-docs` - Air-gapped Setup Guide\n\n### Internal\n- `handleData` - Handle Data\n\n## Notes\n\n- Tool IDs are case-sensitive (use exact names from the reference above)\n- Group IDs are also case-sensitive - note the lowercase `qpdf`\n- Disabling a tool removes it completely from the UI and API\n- Some tools may depend on others - test your configuration\n- Changes require container restart to take effect\n- The [MCP server](doc:configuration/automation/mcp-server) applies its own allow/deny filtering on top of these removals, so an endpoint can be enabled here yet still be hidden from MCP clients", + "sourcePath": "docs/Configuration/Customisation/Endpoint or Feature Customisation.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Customisation/Endpoint or Feature Customisation.md" }, - "configuration/external-database": { - "id": "configuration/external-database", - "title": "External Database", - "section": "configuration", - "markdown": "## Using an External Database\n> **Tier**: Server\n\nIt is possible to use your own external database with Stirling PDF rather than the default H2 database if you wish.\nPostgreSQL is currently the only supported variant, others will be added on request.\n\n### Setting Up External Database Configuration\nYou can configure the new `Datasource` property in your `settings.yml` to connect to your external database:\n\n> #### ⚠️ Note\n> _To use the external database feature, you will need to have a valid enterprise license and set the environment variable `DISABLE_ADDITIONAL_FEATURES` to `false`._\n\n\n \n\n```yaml\n datasource:\n enableCustomDatabase: false\n customDatabaseUrl: jdbc:postgresql://localhost:5432/postgres\n username: postgres\n password: postgres\n type: postgresql\n hostName: localhost\n port: 5432\n name: postgres\n```\n\n- `enableCustomDatabase`: Set this property to `true` to enable use of the custom database **Note: An enterprise license to use this feature**\n- `customDatabaseUrl`: Enter the database connection url for the database here. **Note: If you set the `customDatabaseUrl` you do not need to set the type, hostName, port and name, they will all be automatically derived from the url.**\n- `username`: The username for the database\n- `password`: The password for the database\n\nIf you would like more fine-grained control of the database connection, you can also use the following properties:\n\n#### Fine-grained Database Configuration\n- `type`: The database type. Available options are `h2` and `postgresql`\n- `hostName`: The host name of the database connection url (e.g. 'localhost')\n- `port`: The port number of the database connection url (e.g. 8080)\n- `name`: The name of the custom database. This should match the name you have set for your database\n\n\n \n\n```yaml\nservices:\n db:\n image: 'postgres:17.2-alpine'\n container_name: db\n ports:\n - \"5432:5432\"\n environment:\n POSTGRES_DB: \"stirling_pdf\"\n POSTGRES_USER: \"admin\"\n POSTGRES_PASSWORD: \"stirling\"\n```\n\n- `container_name`: This is the name of your database container. This should match the name of the container under `services` as this is what Docker will use to refer to your database\n- `ports`: Specify the port number for your database. The number on the left is the port number the container will access the database internally. The number on the right is the port number the Stirling PDF app will use to connect to the database externally. Ensure this matches the port number in the connection url for your database otherwise the app will not be able to access it.\n- `POSTGRES_DB`: An environment variable for the database container. Specify the name of the custom database here\n- `POSTGRES_USER`: An environment variable for the database container. Specify the username for the database\n- `POSTGRES_PASSWORD`: An environment variable for the database container. Specify the password for the database\n\nYou will also need to update the Docker configuration in your app in order to connect to the database:\n\n```yaml\nservices:\n stirling-pdf:\n depends_on:\n - db\n environment:\n DISABLE_ADDITIONAL_FEATURES: \"false\" \"true\"\n SYSTEM_DATASOURCE_ENABLECUSTOMDATABASE: \"true\"\n SYSTEM_DATASOURCE_CUSTOMDATABASEURL: \"jdbc:postgresql://db:5432/stirling_pdf\"\n SYSTEM_DATASOURCE_USERNAME: \"admin\"\n SYSTEM_DATASOURCE_PASSWORD: \"stirling\"\n # further configuration\n```\n\n- `depends_on`: This specifies any services that your app will need in order to run. Ensure the name matches the container name for your database\n- `DISABLE_ADDITIONAL_FEATURES`: Set this to `false` to enable security features\n- `SYSTEM_DATASOURCE_ENABLECUSTOMDATABASE`: An environment variable to connect to the database container. Set this to `true` to enable use of the external database\n- `SYSTEM_DATASOURCE_CUSTOMDATABASEURL`: An environment variable to connect to the database container. Set the connection url for the database here. **Note: If you set this url you do not need to set the type, hostName, port and name (namely `SYSTEM_DATASOURCE_TYPE`, `SYSTEM_DATASOURCE_HOSTNAME`, `SYSTEM_DATASOURCE_PORT`, `SYSTEM_DATASOURCE_NAME`), they will all be automatically derived from the url.**\n- `SYSTEM_DATASOURCE_USERNAME`: An environment variable to connect to the database container. Set the username for the database. Ensure this matches the corresponding property in your database container\n- `SYSTEM_DATASOURCE_PASSWORD`: An environment variable to connect to the database container. Set the password for the database. Ensure this matches the corresponding property in your database container\n\nBelow is an example of what your configuration should look like after configuring the custom database:\n\n```yaml\nservices:\n stirling-pdf:\n depends_on:\n - db\n environment:\n DISABLE_ADDITIONAL_FEATURES: \"false\" \"true\"\n SYSTEM_DATASOURCE_ENABLECUSTOMDATABASE: \"true\"\n SYSTEM_DATASOURCE_CUSTOMDATABASEURL: \"jdbc:postgresql://db:5432/stirling_pdf\"\n SYSTEM_DATASOURCE_USERNAME: \"admin\"\n SYSTEM_DATASOURCE_PASSWORD: \"stirling\"\n # further configuration\n\n db:\n image: 'postgres:17.2-alpine'\n container_name: db\n ports:\n - \"5432:5432\"\n environment:\n POSTGRES_DB: \"stirling_pdf\"\n POSTGRES_USER: \"admin\"\n POSTGRES_PASSWORD: \"stirling\"\n```\n\n \n\n\n*Example configuration can be found in [exampleYmlFiles/docker-compose-latest-fat-security-postgres.yml](https://github.com/Stirling-Tools/Stirling-PDF/blob/428b4238e3a7280d71697d994a66174a250387a7/exampleYmlFiles/docker-compose-latest-fat-security-postgres.yml)*", - "sourcePath": "docs/Configuration/External Database.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/External Database.md" - }, - "configuration/extra-settings": { - "id": "configuration/extra-settings", + "configuration/customisation/extra-settings": { + "id": "configuration/customisation/extra-settings", "title": "Custom Settings Configuration", - "section": "configuration", - "markdown": "Stirling PDF provides a `/configs/custom_settings.yml` file where users can configure additional settings beyond the standard configuration. This file follows standard YAML format and supports Spring Boot application properties, allowing you to customize the application without modifying core files.\n\n## Logging Configuration\n\nControl the verbosity of logs by adjusting log levels for different components:\n\n```yaml\nlogging:\n level:\n root: INFO\n org.springframework: WARN\n org.hibernate: WARN\n org.eclipse.jetty: WARN\n stirling.software.SPDF: INFO\n # Enable debug logging for specific components when troubleshooting\n # org.springframework.security.saml2: TRACE\n # org.springframework.security: DEBUG\n # org.opensaml: DEBUG\n```\n\n## Server Configuration\n\nConfigure server behavior including port, address binding, and session timeout:\n\n```yaml\nserver:\n port: 8080 # Default port\n address: 0.0.0.0 # Bind to all interfaces\n servlet:\n context-path: / # Application context path\n session:\n timeout: 30m # Session timeout\n jetty:\n threads:\n max: 200 # Maximum number of request processing threads\n min: 10 # Minimum number of threads always kept running\n connection-idle-timeout: 30000 # Connection idle timeout in milliseconds\n max-http-request-header-size: 65536 # Maximum size of request headers in bytes\n```\n\n### HTTP 431 \"Request Header Fields Too Large\" during SSO/OAuth login\n\nSome SSO/OAuth providers send very large request headers (for example, large cookies or JWTs), which can trigger an **HTTP 431 Request Header Fields Too Large** error during login. Stirling PDF's default limit is `32768` (32 KB); raise it to resolve the error.\n\n\n \n ```yaml\n server:\n jetty:\n max-http-request-header-size: 65536 # bytes (Stirling default: 32768)\n ```\n \n \n ```yaml\n environment:\n SERVER_JETTY_MAX_HTTP_REQUEST_HEADER_SIZE: \"65536\"\n ```\n \n \n ```bash\n docker run -d \\\n -p 8080:8080 \\\n -e SERVER_JETTY_MAX_HTTP_REQUEST_HEADER_SIZE=65536 \\\n stirlingtools/stirling-pdf:latest\n ```\n \n\n\nRaise the value further (for example `131072`) if the error persists, then restart Stirling PDF.\n\n## SSL/TLS Configuration\n\nConfigure HTTPS for secure connections:\n\n\n \n ```yaml\n server:\n port: 8443 # Standard HTTPS port\n ssl:\n enabled: true\n key-store: classpath:keystore.p12 # Path to keystore file\n key-store-password: your-keystore-password\n key-store-type: PKCS12 # Type of keystore\n key-alias: tomcat # Alias of the certificate\n ```\n \n \n ```bash\n SERVER_PORT=8443\n SERVER_SSL_ENABLED=true\n SERVER_SSL_KEY-STORE=classpath:keystore.p12\n SERVER_SSL_KEY-STORE-PASSWORD=your-keystore-password\n SERVER_SSL_KEY-STORE-TYPE=PKCS12\n SERVER_SSL_KEY-ALIAS=tomcat\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n image: stirlingtools/stirling-pdf:latest\n environment:\n SERVER_PORT: 8443\n SERVER_SSL_ENABLED: true\n SERVER_SSL_KEY-STORE: classpath:keystore.p12\n SERVER_SSL_KEY-STORE-PASSWORD: your-keystore-password\n SERVER_SSL_KEY-STORE-TYPE: PKCS12\n SERVER_SSL_KEY-ALIAS: tomcat\n ```\n \n\n\n### Creating a Self-Signed Certificate\n\nTo generate a self-signed certificate for development or testing:\n\n```shell\nkeytool -genkeypair -alias tomcat -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 365\n```\n\n> #### ⚠️ Note\n> _For production use, it's recommended to use a certificate from a trusted Certificate Authority._\n\n## Configuration Examples\n\n### Basic Configuration\n\nA simple configuration that changes the port and adjusts logging levels:\n\n```yaml\n# custom_settings.yml\nserver:\n port: 9000\n\nlogging:\n level:\n root: INFO\n org.springframework: WARN\n org.hibernate: WARN\n stirling.software.SPDF: INFO\n```\n\n### HTTPS Configuration\n\nEnable HTTPS with a custom certificate:\n\n```yaml\n# custom_settings.yml\nserver:\n port: 8443\n ssl:\n enabled: true\n key-store: classpath:keystore.p12\n key-store-password: your-keystore-password\n key-store-type: PKCS12\n key-alias: tomcat\n\nlogging:\n level:\n root: INFO\n org.springframework: WARN\n```\n\n## Troubleshooting Common Issues\n\n### Authentication Issues\n\nIncrease security logging to diagnose authentication problems:\n\n```yaml\nlogging:\n level:\n org.springframework.security: DEBUG\n stirling.software.SPDF.config.security: DEBUG\n```\n\n### SAML/OAuth Issues\n\nIncrease SAML-related logging for SSO troubleshooting:\n\n```yaml\nlogging:\n level:\n org.springframework.security.saml2: TRACE\n org.springframework.security.oauth2: DEBUG\n org.opensaml: DEBUG\n```\n\n### General Application Issues\n\nFor general application issues:\n\n```yaml\nlogging:\n level:\n stirling.software.SPDF: DEBUG\n```\n\n> #### ⚠️ Note\n> _Debug-level logging can significantly increase log volume and may impact performance in production environments. Return logging to normal levels after troubleshooting._", - "sourcePath": "docs/Configuration/Extra-Settings.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Extra-Settings.md" + "section": "configuration/customisation", + "markdown": "Stirling PDF provides a `/configs/custom_settings.yml` file where users can configure additional settings beyond the standard configuration. This file follows standard YAML format and supports Spring Boot application properties, allowing you to customize the application without modifying core files.\n\n## Logging Configuration\n\nControl the verbosity of logs by adjusting log levels for different components:\n\n```yaml\nlogging:\n level:\n root: INFO\n org.springframework: WARN\n org.hibernate: WARN\n org.eclipse.jetty: WARN\n stirling.software.SPDF: INFO\n # Enable debug logging for specific components when troubleshooting\n # org.springframework.security.saml2: TRACE\n # org.springframework.security: DEBUG\n # org.opensaml: DEBUG\n```\n\n## Server Configuration\n\nConfigure server behavior including port, address binding, and session timeout:\n\n```yaml\nserver:\n port: 8080 # Default port\n address: 0.0.0.0 # Bind to all interfaces\n servlet:\n context-path: / # Application context path\n session:\n timeout: 30m # Session timeout\n jetty:\n threads:\n max: 200 # Maximum number of request processing threads\n min: 10 # Minimum number of threads always kept running\n connection-idle-timeout: 30000 # Connection idle timeout in milliseconds\n max-http-request-header-size: 65536 # Maximum size of request headers in bytes\n```\n\n### HTTP 431 \"Request Header Fields Too Large\" during SSO/OAuth login\n\nSome SSO/OAuth providers send very large request headers (for example, large cookies or JWTs), which can trigger an **HTTP 431 Request Header Fields Too Large** error during login. Stirling PDF's default limit is `32768` (32 KB); raise it to resolve the error.\n\n\n \n ```yaml\n server:\n jetty:\n max-http-request-header-size: 65536 # bytes (Stirling default: 32768)\n ```\n \n \n ```yaml\n environment:\n SERVER_JETTY_MAX_HTTP_REQUEST_HEADER_SIZE: \"65536\"\n ```\n \n \n ```bash\n docker run -d \\\n -p 8080:8080 \\\n -e SERVER_JETTY_MAX_HTTP_REQUEST_HEADER_SIZE=65536 \\\n docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n ```\n \n\n\nRaise the value further (for example `131072`) if the error persists, then restart Stirling PDF.\n\n## SSL/TLS Configuration\n\nConfigure HTTPS for secure connections:\n\n\n \n ```yaml\n server:\n port: 8443 # Standard HTTPS port\n ssl:\n enabled: true\n key-store: classpath:keystore.p12 # Path to keystore file\n key-store-password: your-keystore-password\n key-store-type: PKCS12 # Type of keystore\n key-alias: tomcat # Alias of the certificate\n ```\n \n \n ```bash\n SERVER_PORT=8443\n SERVER_SSL_ENABLED=true\n SERVER_SSL_KEY-STORE=classpath:keystore.p12\n SERVER_SSL_KEY-STORE-PASSWORD=your-keystore-password\n SERVER_SSL_KEY-STORE-TYPE=PKCS12\n SERVER_SSL_KEY-ALIAS=tomcat\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n environment:\n SERVER_PORT: 8443\n SERVER_SSL_ENABLED: true\n SERVER_SSL_KEY-STORE: classpath:keystore.p12\n SERVER_SSL_KEY-STORE-PASSWORD: your-keystore-password\n SERVER_SSL_KEY-STORE-TYPE: PKCS12\n SERVER_SSL_KEY-ALIAS: tomcat\n ```\n \n\n\n### Creating a Self-Signed Certificate\n\nTo generate a self-signed certificate for development or testing:\n\n```shell\nkeytool -genkeypair -alias tomcat -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 365\n```\n\n> **⚠️ Warning: Note**\n>\n> For production use, it's recommended to use a certificate from a trusted Certificate Authority.\n\n\n## Configuration Examples\n\n### Basic Configuration\n\nA simple configuration that changes the port and adjusts logging levels:\n\n```yaml\n# custom_settings.yml\nserver:\n port: 9000\n\nlogging:\n level:\n root: INFO\n org.springframework: WARN\n org.hibernate: WARN\n stirling.software.SPDF: INFO\n```\n\n### HTTPS Configuration\n\nEnable HTTPS with a custom certificate:\n\n```yaml\n# custom_settings.yml\nserver:\n port: 8443\n ssl:\n enabled: true\n key-store: classpath:keystore.p12\n key-store-password: your-keystore-password\n key-store-type: PKCS12\n key-alias: tomcat\n\nlogging:\n level:\n root: INFO\n org.springframework: WARN\n```\n\n## Troubleshooting Common Issues\n\n### Authentication Issues\n\nIncrease security logging to diagnose authentication problems:\n\n```yaml\nlogging:\n level:\n org.springframework.security: DEBUG\n stirling.software.SPDF.config.security: DEBUG\n```\n\n### SAML/OAuth Issues\n\nIncrease SAML-related logging for SSO troubleshooting:\n\n```yaml\nlogging:\n level:\n org.springframework.security.saml2: TRACE\n org.springframework.security.oauth2: DEBUG\n org.opensaml: DEBUG\n```\n\n### General Application Issues\n\nFor general application issues:\n\n```yaml\nlogging:\n level:\n stirling.software.SPDF: DEBUG\n```\n\n> **⚠️ Warning: Note**\n>\n> Debug-level logging can significantly increase log volume and may impact performance in production environments. Return logging to normal levels after troubleshooting.", + "sourcePath": "docs/Configuration/Customisation/Extra-Settings.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Customisation/Extra-Settings.md" }, - "configuration/fail2ban": { - "id": "configuration/fail2ban", - "title": "Fail2Ban Integration", - "section": "configuration", - "markdown": "## Fail2Ban Setup for Stirling PDF\nThis document provides instructions on how to set up Fail2Ban with Stirling PDF to protect against unauthorized login attempts. (Note Stirling PDF blocks IPs after a set retry count regardless of Fail2Ban, This configuration is only useful for users specifically wanting Fail2Ban configuration)\n\n## How does Fail2Ban Work with Stirling PDF\nStirling PDF logs failed authentication attempts to a log file which Fail2Ban monitors. When it detects multiple failed login attempts from the same IP address, Fail2Ban automatically blocks that IP address for a configured period of time.\n\n\n## Prerequisites\n- Fail2Ban installed on your system\n- Access to Stirling PDF log directory\n- Security settings configured:\n\n\n \n ```yaml\n security:\n enableLogin: true # Login must be enabled for Fail2Ban integration\n loginAttemptCount: -1 # Set to -1 when using Fail2Ban recommended but not required\n ```\n \n \n ```bash\n SECURITY_ENABLELOGIN=true\n SECURITY_LOGINATTEMPTCOUNT=-1\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n environment:\n SECURITY_ENABLELOGIN: true\n SECURITY_LOGINATTEMPTCOUNT: -1\n ```\n \n\n\n### Important Configuration Notes\n- The `enableLogin` setting must be set to `true` as Fail2Ban integration requires authentication to be active\n- When using Fail2Ban, set `loginAttemptCount` to `-1` to disable the built-in account locking mechanism and let Fail2Ban handle login attempt management\n- For more details on security configuration options, refer to the [System and Security](doc:configuration/system-and-security) documentation\n\n## Configuration\n\n### Log File Location\nThe log file location containing the failed authentication messages depends on your installation type:\n\n- **Default/Docker Installation**: ``./logs/invalid-auths.log``\n- **Windows Desktop**: ``%APPDATA%\\Stirling-PDF\\logs\\invalid-auths.log``\n- **MacOS Desktop**: ``~/Library/Application Support/Stirling-PDF/logs/invalid-auths.log``\n- **Linux Desktop**: ``~/.config/Stirling-PDF/logs/invalid-auths.log``\n\n### Example Fail2Ban Filter\n`/etc/fail2ban/filter.d/stirling-pdf.conf`\n```ini\n[Definition]\nfailregex = Failed login attempt from IP: \n```\n\n### Example Jail Configuration\n`/etc/fail2ban/jail.local`\n```ini\n[stirling-pdf]\nenabled = true\nfilter = stirling-pdf\nlogpath = /logs/invalid-auths.log\nmaxretry = 5\nfindtime = 300\nbantime = 3600\n```\n\nConfiguration parameters:\n- `maxretry`: Number of failed attempts before ban (default: 5)\n- `findtime`: Time window for failed attempts in seconds (default: 300 seconds / 5 minutes)\n- `bantime`: Duration of the ban in seconds (default: 3600 seconds / 1 hour)\n\n\n### Ensure access to Logs path\n\n \n Modify your `docker-compose.yml` to expose the log directory:\n ```yaml\n services:\n stirling-pdf:\n volumes:\n - ./logs:/logs\n ```\n \n \n Add the volume mount to your Docker run command:\n ```bash\n -v ./logs:/logs\n ```", - "sourcePath": "docs/Configuration/Fail2Ban.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Fail2Ban.md" - }, - "configuration/file-sharing-and-storage": { - "id": "configuration/file-sharing-and-storage", - "title": "File Sharing and Storage", - "description": "Configure server-side file storage, sharing, and storage quotas", - "section": "configuration", - "markdown": "> **⚠️ Warning: [Alpha Feature]**\n>\n> File Sharing and Storage is currently in **alpha**. Functionality may change, and some features are incomplete. Use in production at your own risk.\n\n\nStirling PDF can store files on the server and let users share them with each other. Files can be shared directly with specific users or via shareable links. Admins can set storage quotas to control disk usage.\n\nBasic local-disk storage and sharing need **no license** - just turn on `security.enableLogin` and `storage.enabled`. Only the **database** and **s3** storage providers require a Pro/Enterprise license. See [Modes](doc:modes-and-licensing) for what each deploy mode includes; self-hosted instances never use credits.\n\n> **💡 Tip: Already set up?**\n>\n> If your admin has turned storage on and you just want to use it, skip ahead to [The My Files Page](#the-my-files-page) and [Sharing Files](#sharing-files). The setup sections in between (storage providers, S3, quotas) are for whoever runs the server.\n\n\n---\n\n## What You Can Do\n\n- **Store files server-side** -- upload PDFs and other files that persist across sessions\n- **Share with specific users** -- grant other registered users access to your files with configurable permissions\n- **Share via link** -- generate a shareable link that any logged-in user with the link can access\n- **Control access levels** -- assign Editor, Commenter, or Viewer roles to shared users\n- **Set storage quotas** -- limit storage per user, per file, or system-wide\n- **Audit access** -- see who accessed your shared links and when\n- **Automatic cleanup** -- expired share links and orphaned files are cleaned up daily\n\n---\n\n## Prerequisites\n\n- **Authentication must be enabled** (`security.enableLogin: true`)\n- For share links: `system.frontendUrl` must be set to your instance URL\n- For email notifications when sharing: `mail.enabled: true` with valid SMTP configuration\n\n---\n\n## Enabling File Storage\n\n\n \n ```yaml\n storage:\n enabled: true\n provider: local # 'local', 'database', or 's3'\n local:\n basePath: './storage' # Filesystem path (local provider only)\n ```\n \n \n ```bash\n STORAGE_ENABLED=true\n STORAGE_PROVIDER=local\n STORAGE_LOCAL_BASEPATH=./storage\n ```\n \n\n\n### Storage Providers\n\n| Provider | Config Value | License | Description | Best For |\n|----------|-------------|---------|-------------|----------|\n| **Local Filesystem** | `local` | Free | Files stored on disk under `basePath` | Most deployments, large files |\n| **Database** | `database` | Pro/Enterprise | Files stored as BLOBs in the database | Simple setups where you want everything in one place |\n| **S3-Compatible** | `s3` | Pro/Enterprise | Files stored in an S3-compatible object store | Multi-node clusters, cloud object storage |\n\n> **💡 Tip**\n>\n> The **local** provider is recommended for most deployments and requires no license. It handles large files well and keeps database size manageable. The **database** provider is convenient but uses more memory with large files. The **s3** provider is for object-storage and multi-node deployments. The **database** and **s3** providers require a Pro/Enterprise license.\n\n\n### Docker Volume Mount\n\nWhen using the local provider with Docker, mount the storage directory so files persist across container restarts:\n\n```yaml\nvolumes:\n - ./stirling-storage:/storage\n```\n\n---\n\n## S3-Compatible Object Storage\n\n> **ℹ️ Info: [Pro/Enterprise]**\n>\n> The **s3** storage provider requires a valid Pro or Enterprise license.\n\n\nSet `storage.provider: s3` to store user uploads in any S3-compatible object store. The same `storage.s3.*` block is also used by the cluster artifact store (see below).\n\n\n \n ```yaml\n storage:\n enabled: true\n provider: s3\n s3:\n endpoint: \"\" # blank = AWS regional default; otherwise full URL incl. https://\n bucket: my-bucket # required\n region: us-east-1\n accessKey: \"\" # blank = fall back to AWS DefaultCredentialsProvider (env / profile / IMDS)\n secretKey: \"\"\n pathStyleAccess: false # true for MinIO and Supabase; false for AWS/R2/most CDNs\n allowPrivateEndpoints: false # SSRF guard - see below\n requestChecksumCalculation: WHEN_SUPPORTED # WHEN_SUPPORTED | WHEN_REQUIRED | DISABLED\n responseChecksumValidation: WHEN_SUPPORTED # WHEN_SUPPORTED | WHEN_REQUIRED | DISABLED\n ```\n \n \n ```bash\n STORAGE_ENABLED=true\n STORAGE_PROVIDER=s3\n STORAGE_S3_ENDPOINT=\n STORAGE_S3_BUCKET=my-bucket\n STORAGE_S3_REGION=us-east-1\n STORAGE_S3_ACCESSKEY=\n STORAGE_S3_SECRETKEY=\n STORAGE_S3_PATHSTYLEACCESS=false\n STORAGE_S3_ALLOWPRIVATEENDPOINTS=false\n STORAGE_S3_REQUESTCHECKSUMCALCULATION=WHEN_SUPPORTED\n STORAGE_S3_RESPONSECHECKSUMVALIDATION=WHEN_SUPPORTED\n ```\n \n\n\n### S3 Configuration Keys\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `endpoint` | _(blank)_ | Blank uses the AWS regional default. For other vendors, the full URL including `https://`. |\n| `bucket` | _(blank)_ | Required. The bucket that holds the stored files. |\n| `region` | `us-east-1` | Region of the bucket. |\n| `accessKey` | _(blank)_ | Static access key. Blank falls back to the AWS `DefaultCredentialsProvider` (env vars, profile, or IMDS). |\n| `secretKey` | _(blank)_ | Static secret key. Used together with `accessKey`. |\n| `pathStyleAccess` | `false` | Use path-style (`endpoint/bucket/key`) instead of virtual-host addressing. `true` for MinIO and Supabase. |\n| `allowPrivateEndpoints` | `false` | SSRF guard. When `false`, an endpoint that resolves to a loopback, link-local, or private (RFC1918) address is rejected at startup. Set `true` to opt in (for example in-cluster MinIO). Leave `false` for any internet-facing vendor. |\n| `requestChecksumCalculation` | `WHEN_SUPPORTED` | `WHEN_SUPPORTED`, `WHEN_REQUIRED`, or `DISABLED`. Set `WHEN_REQUIRED` if your vendor rejects auto-added `x-amz-checksum-*` headers (older Backblaze B2, some R2 corner cases). |\n| `responseChecksumValidation` | `WHEN_SUPPORTED` | `WHEN_SUPPORTED`, `WHEN_REQUIRED`, or `DISABLED`. Set `WHEN_REQUIRED` if you see false-positive checksum-mismatch errors on GET from a vendor that never returns checksum headers. |\n\n> **⚠️ Warning: [SSRF guard]**\n>\n> `allowPrivateEndpoints` defaults to `false`. The server resolves the configured `endpoint` host and refuses to start if it points at a loopback, link-local, or private IP. This blocks an admin-supplied endpoint from being pointed at the cloud metadata service (for example `http://169.254.169.254/`) to exfiltrate instance-role credentials. Only set it to `true` for a trusted in-cluster store such as MinIO.\n\n\n### Per-Vendor Cheat Sheet\n\n| Vendor | `endpoint` | `region` | `pathStyleAccess` | Notes |\n|--------|-----------|----------|-------------------|-------|\n| **AWS S3** | _(blank)_ | your region | `false` | Uses the AWS regional default. |\n| **MinIO** (in-cluster) | `http://minio:9000` | `us-east-1` | `true` | Also set `allowPrivateEndpoints: true`. |\n| **Cloudflare R2** | `https://.r2.cloudflarestorage.com` | `auto` | `false` | If uploads fail with `unsupported header x-amz-checksum-*`, set `requestChecksumCalculation: WHEN_REQUIRED`. |\n| **Supabase Storage** | `https://.supabase.co/storage/v1/s3` | your project region | `true` | Non-ASCII display filenames are fine - the storage key is opaque. |\n| **Backblaze B2** | `https://s3..backblazeb2.com` | your region | `false` | On B2 deployments older than July 2025, if uploads return `Unsupported header x-amz-checksum-crc32`, set `requestChecksumCalculation: WHEN_REQUIRED`. |\n| **DigitalOcean Spaces** | `https://.digitaloceanspaces.com` | your region | `false` | 5 GB per-object cap (regardless of multipart). |\n\n### Sharing Credentials with the Cluster Artifact Store\n\nThe `storage.s3.*` block is used in two places: the **s3** storage provider (persistent user uploads) and the cluster artifact store when `cluster.artifactStore: s3` (transient multi-node job artifacts). When both use S3 they reuse the same credentials and bucket. The cluster store writes under a separate key prefix (`cluster.s3.keyPrefix`, default `transient/`) so a single bucket can host both persistent uploads and transient artifacts without collisions. Multi-node deployments must set `cluster.artifactStore: s3`.\n\n---\n\n## Enabling File Sharing\n\n\n \n ```yaml\n storage:\n enabled: true\n sharing:\n enabled: true # Master switch for all sharing\n linkEnabled: true # Enable shareable links\n emailEnabled: true # Send email notifications when sharing\n linkExpirationDays: 3 # Days until share links expire\n ```\n \n \n ```bash\n STORAGE_SHARING_ENABLED=true\n STORAGE_SHARING_LINKENABLED=true\n STORAGE_SHARING_EMAILENABLED=true\n STORAGE_SHARING_LINKEXPIRATIONDAYS=3\n ```\n \n\n\n### Feature Dependencies\n\n| Feature | What It Needs |\n|---------|---------------|\n| File Storage | `security.enableLogin: true` |\n| File Sharing | Storage enabled |\n| Shareable Links | Sharing enabled + `system.frontendUrl` set |\n| Email Notifications | Sharing enabled + `mail.enabled: true` |\n| Shared Signing | Storage enabled + `storage.signing.enabled: true` |\n\n---\n\n## Storage Quotas\n\nControl how much storage space is available.\n\n\n \n ```yaml\n storage:\n quotas:\n maxStorageMbPerUser: -1 # Per-user cap in MB (-1 = unlimited)\n maxStorageMbTotal: -1 # Total system cap in MB (-1 = unlimited)\n maxFileMb: -1 # Max size per upload in MB (-1 = unlimited)\n ```\n \n \n ```bash\n STORAGE_QUOTAS_MAXSTORAGEMBPERUSER=500\n STORAGE_QUOTAS_MAXSTORAGEMBTOTAL=10000\n STORAGE_QUOTAS_MAXFILEMB=100\n ```\n \n\n\nQuotas are checked before a file is stored. When replacing an existing file, only the size difference counts against the quota.\n\n---\n\n## Access Roles\n\nWhen sharing a file, you choose what level of access to grant:\n\n| Role | Can View/Download | Can Replace File | In Signing Workflows |\n|------|-------------------|-----------------|---------------------|\n| **Editor** | Yes | Yes | Can sign |\n| **Commenter** | Yes | No | Can sign |\n| **Viewer** | Yes | No | Read-only |\n\nThe file **owner** always has full access. The default role is **Editor**.\n\nThe difference between Commenter and Viewer only matters in [Shared Signing](doc:functionality/security/shared-signing) workflows -- both are read-only for regular file sharing.\n\n---\n\n## The My Files Page\n\nWhen your admin has turned storage on, you get a **My Files** page (find it at `/files` once you are logged in). It is your personal space on the server for keeping documents that stick around between sessions, instead of living only in your browser tab.\n\nOn the My Files page you can:\n\n- **Upload** documents to keep them on the server\n- **Organize them into folders**, including folders inside folders, and drag files between them\n- **Preview** a stored file right in the browser without downloading it\n- **Rename, move, and delete** files and folders\n- **Jump around quickly** using the folder sidebar on the left\n- **Personalize folders** with colors and thumbnails so they are easy to spot\n- **See where each file lives** - every item shows a small badge telling you whether it is in your current browser session or saved on the server\n\nYour folders are private to you. Folders themselves are not shared; you share individual files instead (see [Sharing Files](#sharing-files) below).\n\n---\n\n## Sharing Files\n\nYou can share a file from the **My Files** page, or from the **Share** button in the top bar of the editor workbench while a file is open.\n\n### Share with a Specific User\n\nFrom the file manager, select a file and share it with another user by their username or email address. You can choose the access role when sharing.\n\nIf you enter an email address for someone who doesn't have an account, the system will create a share link and email it to them (if email notifications are enabled).\n\n### Share via Link\n\nGenerate a shareable link for any file you own. Anyone who is logged in and has the link can access the file. Links expire automatically based on your `linkExpirationDays` setting (default: 3 days).\n\nYou can revoke a share link at any time, which immediately removes access and deletes all access records for that link.\n\nThe share link URL follows the format:\n```\nhttps://your-stirling-instance.com/share/{token}\n```\n\n> **ℹ️ Info**\n>\n> Share links require the recipient to be logged in. There is no anonymous or public access -- the link is an additional credential on top of authentication.\n\n\n### Access History\n\nFor any share link you've created, you can view who accessed it, whether they viewed or downloaded the file, and when.\n\n---\n\n## Security\n\n- **All endpoints require authentication** -- there is no anonymous file access\n- **Owner-only controls** -- only the file owner can update, delete, or manage sharing\n- **Random tokens** -- share link tokens are cryptographically random UUIDs\n- **Automatic expiration** -- expired links return an error and are cleaned up daily\n- **Revocation** -- owners can revoke any share link immediately\n- **Access auditing** -- every share link access is recorded with user, action type, and timestamp\n\n---\n\n## Known Limitations\n\n- Share links require `system.frontendUrl` to be configured\n- Share links require the user to be logged in -- there is no public/anonymous access\n- The database storage provider uses more memory with large files (use the local provider for large deployments)\n\n---\n\n## Troubleshooting\n\n### \"Storage is disabled\"\n- Verify `storage.enabled: true` in your settings\n- Verify `security.enableLogin: true`\n\n### \"Share links are disabled\"\n- Verify `storage.sharing.linkEnabled: true`\n- Verify `system.frontendUrl` is set to your instance URL\n\n### \"Email sharing is disabled\"\n- Verify `storage.sharing.emailEnabled: true`\n- Verify `mail.enabled: true` with valid SMTP settings\n\n### Share link returns 404 Not Found\n- The link has expired or does not exist. Expired and missing links both return **404 Not Found**. The file owner needs to create a new one.\n\n### Quota exceeded (HTTP 413)\n- A file or upload that exceeds a configured quota is rejected with **413 Payload Too Large** (per-file, per-user, and total-storage caps all use this status)\n- Increase `maxStorageMbPerUser`, `maxStorageMbTotal`, or `maxFileMb`, or delete unused files to free up space\n\n---\n\n## Developer Reference: Storage API\n\nThis section is for developers and admins automating storage outside the web app. If you just want to upload, organize, and share files, everything above is done from the **My Files** page - you do not need any of this.\n\nThe full storage and sharing endpoints are listed below. See [API Documentation](doc:api) for authentication and general usage.\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| POST | `/api/v1/storage/files` | Upload file |\n| PUT | `/api/v1/storage/files/{id}` | Update file (owner only) |\n| GET | `/api/v1/storage/files` | List accessible files |\n| GET | `/api/v1/storage/files/{id}` | Get file metadata |\n| GET | `/api/v1/storage/files/{id}/download` | Download file |\n| DELETE | `/api/v1/storage/files/{id}` | Delete file (owner only) |\n| POST | `/api/v1/storage/files/{id}/shares/users` | Share with user |\n| DELETE | `/api/v1/storage/files/{id}/shares/users/{username}` | Revoke user share |\n| DELETE | `/api/v1/storage/files/{id}/shares/self` | Leave shared file |\n| POST | `/api/v1/storage/files/{id}/shares/links` | Create share link |\n| DELETE | `/api/v1/storage/files/{id}/shares/links/{token}` | Revoke share link |\n| GET | `/api/v1/storage/share-links/{token}` | Access via share link |\n| GET | `/api/v1/storage/share-links/{token}/metadata` | Get share link info |\n| GET | `/api/v1/storage/share-links/accessed` | List your accessed links |\n| GET | `/api/v1/storage/files/{id}/shares/links/{token}/accesses` | Access history (owner only) |\n\n### Folder Endpoints\n\nThese are the endpoints behind the folders on the **My Files** page. All operations are scoped to the authenticated user.\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| GET | `/api/v1/storage/folders` | List your folders |\n| POST | `/api/v1/storage/folders` | Create a folder |\n| PATCH | `/api/v1/storage/folders/{folderId}` | Update a folder (name, appearance) |\n| DELETE | `/api/v1/storage/folders/{folderId}` | Delete a folder |\n| PATCH | `/api/v1/storage/files/{fileId}/folder` | Move a single file to a folder (or to root when `folderId` is null) |\n| PATCH | `/api/v1/storage/files/folder` | Bulk-move files to a folder (up to 1000 per request) |\n\n---\n\n## Related\n\n- [Shared Signing](doc:functionality/security/shared-signing) -- Collaborative multi-participant document signing\n- [Certificate Signing](doc:functionality/security/certificate-signing) -- Individual certificate signing\n- [System and Security Settings](doc:configuration/system-and-security) -- JWT, sessions, server certificates", - "sourcePath": "docs/Configuration/File Sharing and Storage.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/File Sharing and Storage.md" - }, - "configuration/folderscanning": { - "id": "configuration/folderscanning", - "title": "Folder Scanning", - "section": "configuration", - "markdown": "## User Guide for Local Directory Scanning and File Processing\n\nFolder scanning uses settings configured from our pipeline tool, it is advised you first read the [Pipeline Guide](doc:configuration/pipeline)\n### Setting Up Watched Folders\n\n- Create a folder where you want your files to be monitored. This is your 'watched folder'.\n- The default directory for this is `./pipeline/watchedFolders/`.\n- Place any directories you want to be scanned into this folder. This folder should contain multiple folders, each for their own tasks and pipelines.\n\n### Configuring Processing with JSON Files\n\n- In each directory you want processed (e.g., `./pipeline/watchedFolders/officePrinter`), include a JSON configuration file.\n- This JSON file should specify how you want the files in the directory to be handled (e.g., what operations to perform on them). This can be made, configured, and downloaded from the Stirling PDF Pipeline interface. For JSON creation guide please see [Pipeline setup](doc:configuration/pipeline)\n\n### Automatic Scanning and Processing\n\n- The system automatically checks the watched folder every minute for new directories and files to process.\n- When a directory with a valid JSON configuration file is found, it begins processing the files inside according to the configuration.\n\n### Processing Steps\n\n- Files in each directory are processed according to the instructions in the JSON file.\n- This might involve file conversions, data filtering, renaming files, etc. If the output of a step is a zip, this zip will be automatically unzipped as it passes to the next process.\n\n### Results and Output\n\n- After processing, the results are saved in a specified output location. This could be a different folder or location as defined in the JSON file or the default location `./pipeline/finishedFolders/`.\n- Each processed file is named and organized according to the rules set in the JSON configuration.\n\n### Completion and Cleanup\n\n- Once processing is complete, the original files in the watched folder's directory are removed.\n- You can find the processed files in the designated output location.\n\n### Error Handling\n\n- If there's an error during processing, the system will not delete the original files, allowing you to check and retry if necessary.\n\n### User Interaction\n\n- As a user, your main tasks are to set up the watched folders, place directories with files for processing, and create the corresponding JSON configuration files.\n- The system handles the rest, including scanning, processing, and outputting results.", - "sourcePath": "docs/Configuration/FolderScanning.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/FolderScanning.md" - }, - "configuration/google-drive-file-picker": { - "id": "configuration/google-drive-file-picker", - "title": "Google Drive File Picker", - "section": "configuration", - "markdown": "> **Tier**: Server\n\nStirling PDF allows users to select Files for processing through tools via google drive.\n\n## Google Api Access\nTo enable this features for your users, you must first set up your Google environment. This includes creating a Google Cloud project. Follow the **Setting up your environment** section of [this guide](https://developers.google.com/workspace/drive/picker/guides/overview#setup) to do so.\n\n## Stirling PDF configuration\n\n```yaml\npremium:\n ...\n enabled: true # Enable license key checks for pro/enterprise features\n proFeatures:\n ...\n googleDrive:\n enabled: true\n clientId: \n apiKey: \n appId: \n```\n- `premium.enabled`: Set to `true` to enable premium features. \n- `googleDrive.enabled`: Set to `true` to enable google drive file picker features. \n- `googleDrive.clientId`: Your Google web app's client ID. [Go to Credentials](https://console.cloud.google.com/apis/credentials) and Click **Create credentials > OAuth client ID**.\n- `googleDrive.apiKey`: API key for google api access. [Go to Credentials](https://console.cloud.google.com/apis/credentials) and Click **Create credentials > API key**.\n- `googleDrive.appId`: Google drive app ID also known as your Project Number Found in your [IAM&Admin Project Settings](https://console.cloud.google.com/iam-admin/settings)\n\n\n > #### ⚠️ Note\n> _You must set the Authorized Javascript origins for your OAuth client ID to include your Stirling PDF host domain or IP address._\n\n## Configurations Examples\nBelow are examples of the full configuration for enabling the google Drive Picker:\n\n\n \n ```yaml\n premium:\n enabled: true # Enable license key checks for pro/enterprise features\n proFeatures:\n googleDrive:\n enabled: true\n clientId: \n apiKey: \n appId: \n ```\n \n \n ```bash\n export PREMIUM_ENABLED=true\n export PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_ENABLED=true\n export PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_CLIENT_ID=\"\"\n export PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_API_KEY=\"\"\n export PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_APP_ID=\"\"\n ```\n \n \n ```bash\n -e PREMIUM_ENABLED=true \\\n -e PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_ENABLED=true \\\n -e PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_CLIENT_ID=\"\" \\\n -e PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_API_KEY=\"\" \\\n -e PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_APP_ID=\"\" \\\n ```\n \n \n ```yaml\n environment:\n PREMIUM_ENABLED: true\n PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_ENABLED: true\n PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_CLIENT_ID: \n PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_API_KEY: \n PREMIUM_PRO_FEATURES_GOOGLE_DRIVE_APP_ID: \n ```", - "sourcePath": "docs/Configuration/Google Drive File Picker.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Google Drive File Picker.md" - }, - "configuration/keyboard-shortcuts": { - "id": "configuration/keyboard-shortcuts", + "configuration/customisation/keyboard-shortcuts": { + "id": "configuration/customisation/keyboard-shortcuts", "title": "Keyboard Shortcuts", - "section": "configuration", + "section": "configuration/customisation", "markdown": "Stirling PDF supports keyboard shortcuts for quick tool access and PDF viewer navigation. Custom shortcuts are saved in your browser's local storage, with plans to bind to your account in the future.\n\n## Tool Shortcuts\n\n### Default shortcuts\n\nTools in the Quick Access bar (the \"Recommended Tools\" category) automatically receive shortcuts:\n\n| Windows / Linux | Mac | Action |\n|---|---|---|\n| `Ctrl + Alt + 1` | `⌘ + ⌥ + 1` | First Quick Access tool |\n| `Ctrl + Alt + 2` | `⌘ + ⌥ + 2` | Second Quick Access tool |\n| `Ctrl + Alt + 3` | `⌘ + ⌥ + 3` | Third Quick Access tool |\n| … up to `9` | | |\n\nAll other tools have no default shortcut but can be assigned one.\n\n### Customising shortcuts\n\n1. Open **Settings** (gear icon at the bottom of the Quick Access bar)\n2. Go to **Keyboard Shortcuts**\n3. Find the tool you want\n4. Click **Change shortcut**, then press your key combination\n5. Press **Esc** to cancel\n\nYour shortcut must include at least one modifier key (`Ctrl`, `Alt`, or `Cmd`). `Shift` alone does not count as a modifier. If the combination is already taken by another tool, you'll see a conflict warning.\n\n### Resetting a shortcut\n\nClick **Reset** next to any tool to restore its default. If a tool has no default, Reset clears the custom binding.\n\n## PDF Viewer Shortcuts\n\nThese shortcuts work when the PDF viewer is active (hovered), except for Print, Select all text, and zoom which work whenever the viewer is open.\n\n### Modifier shortcuts (Ctrl / Cmd + key)\n\n| Windows / Linux | Mac | Action |\n|---|---|---|\n| `Ctrl + P` | `⌘ + P` | Print (works globally when viewer is mounted) |\n| `Ctrl + A` | `⌘ + A` | Select all text (works globally when viewer is open) |\n| `Ctrl + F` | `⌘ + F` | Open / focus search |\n| `Ctrl + S` | `⌘ + S` | Save / apply changes |\n| `Ctrl + Z` | `⌘ + Z` | Undo |\n| `Ctrl + Shift + Z` | `⌘ + ⇧ + Z` | Redo |\n| `Ctrl + Y` | `⌘ + Y` | Redo (alternative) |\n| `Ctrl + =` / `Ctrl + +` | `⌘ + =` / `⌘ + +` | Zoom in |\n| `Ctrl + -` | `⌘ + -` | Zoom out |\n| `Ctrl + 0` | `⌘ + 0` | Reset zoom (fit width) |\n\n### Navigation shortcuts (no modifier needed)\n\n| Key | Action |\n|---|---|\n| `Home` | Jump to first page |\n| `End` | Jump to last page |\n| `Page Up` | Previous page |\n| `Page Down` | Next page |\n| `Escape` | Close search |\n\n### Rotate tool\n\nWhen the Rotate tool is active:\n\n| Key | Action |\n|---|---|\n| `←` (Left Arrow) | Rotate left |\n| `→` (Right Arrow) | Rotate right |\n\n## Desktop App\n\nThe desktop app adds one extra shortcut:\n\n| Windows / Linux | Mac | Action |\n|---|---|---|\n| `Ctrl + S` | `⌘ + S` | Save selected files to disk |", - "sourcePath": "docs/Configuration/Keyboard-Shortcuts.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Keyboard-Shortcuts.md" + "sourcePath": "docs/Configuration/Customisation/Keyboard-Shortcuts.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Customisation/Keyboard-Shortcuts.md" }, - "configuration/libreoffice-parallel-processing": { - "id": "configuration/libreoffice-parallel-processing", - "title": "LibreOffice Parallel Processing", - "description": "Configure multiple LibreOffice instances for parallel document conversion", - "section": "configuration", - "markdown": "Stirling PDF uses LibreOffice for converting office documents (DOCX, XLSX, PPTX, etc.) to PDF and other formats. LibreOffice processes each conversion in a single thread, meaning one conversion uses one CPU core at 100% regardless of how many cores are available. To process multiple conversions at the same time, you need to run multiple LibreOffice instances.\n\n---\n\n## Local UNO Server Pool\n\nBy default, Stirling PDF manages a local pool of UNO (Universal Network Objects) server instances. The number of instances is controlled by the `libreOfficeSessionLimit` setting.\n\n\n \n ```yaml\n processExecutor:\n autoUnoServer: true\n sessionLimit:\n libreOfficeSessionLimit: 4 # Run 4 LibreOffice instances\n ```\n \n \n ```bash\n PROCESS_EXECUTOR_SESSION_LIMIT_LIBRE_OFFICE_SESSION_LIMIT=4\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n environment:\n PROCESS_EXECUTOR_SESSION_LIMIT_LIBRE_OFFICE_SESSION_LIMIT: 4\n ```\n \n\n\nA reasonable starting point is one instance per 2 CPU cores. See [Host resource requirements](#host-resource-requirements) for memory and storage sizing.\n\n> **ℹ️ Info**\n>\n> The default `libreOfficeSessionLimit` is `1`, meaning only one conversion runs at a time. If you see conversions queuing up or running slowly, increasing this is the first thing to try.\n\n\n### Throughput expectations\n\nPer-conversion time varies from sub-second (small DOCX) to tens of seconds (complex PPTX, large spreadsheets). Pool throughput scales roughly linearly with worker count up to host CPU saturation - benchmark a representative document before sizing.\n\n---\n\n## Remote unoservers\n\nFor larger deployments, or when you want to isolate LibreOffice from the main application, run UNO servers as separate containers and configure Stirling PDF to connect to them remotely. This is a two-step setup: start the unoserver containers, then point Stirling PDF at them.\n\n### Starting unoserver Containers\n\nEach container is a single worker that listens internally on port `2003`. Expose it on a different host port per instance if you want to reach them from outside Docker or from another host.\n\n\n \n ```yaml\n services:\n unoserver1:\n image: ghcr.io/stirling-tools/stirling-unoserver:latest\n ports:\n - \"2003:2003\"\n\n unoserver2:\n image: ghcr.io/stirling-tools/stirling-unoserver:latest\n ports:\n - \"2004:2003\"\n ```\n Add these alongside your `stirling-pdf` service. Host-port mappings are only required if Stirling PDF runs outside Docker, on a different host, or on a separate Docker network.\n \n \n ```bash\n docker run -d --name unoserver1 -p 2003:2003 \\\n ghcr.io/stirling-tools/stirling-unoserver:latest\n\n docker run -d --name unoserver2 -p 2004:2003 \\\n ghcr.io/stirling-tools/stirling-unoserver:latest\n ```\n \n\n\nFor tunable options (timeouts, periodic recycling, CJK fonts), see [The `stirling-unoserver` Image](#the-stirling-unoserver-image) below.\n\n### Connecting Stirling PDF to Remote Endpoints\n\nOnce your unoserver containers are running, set `autoUnoServer` to `false` and point Stirling PDF at them:\n\n\n \n ```yaml\n processExecutor:\n autoUnoServer: false\n unoServerEndpoints:\n - host: \"unoserver1\"\n port: 2003\n hostLocation: \"remote\"\n protocol: \"http\"\n - host: \"unoserver2\"\n port: 2003\n hostLocation: \"remote\"\n protocol: \"http\"\n - host: \"unoserver3\"\n port: 2003\n hostLocation: \"remote\"\n protocol: \"http\"\n ```\n \n \n ```bash\n PROCESS_EXECUTOR_AUTO_UNO_SERVER=false\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_0_HOST=unoserver1\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_0_PORT=2003\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_0_HOST_LOCATION=remote\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_0_PROTOCOL=http\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_1_HOST=unoserver2\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_1_PORT=2003\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_1_HOST_LOCATION=remote\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_1_PROTOCOL=http\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n ports:\n - \"8080:8080\"\n environment:\n PROCESS_EXECUTOR_AUTO_UNO_SERVER: \"false\"\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_0_HOST: \"unoserver1\"\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_0_PORT: \"2003\"\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_0_HOST_LOCATION: \"remote\"\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_1_HOST: \"unoserver2\"\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_1_PORT: \"2003\"\n PROCESS_EXECUTOR_UNO_SERVER_ENDPOINTS_1_HOST_LOCATION: \"remote\"\n ```\n \n\n\nTo add more endpoints, add additional entries to the `unoServerEndpoints` list in settings.yml, or for environment variables, increment the index number (e.g. `_0_` for the first, `_1_` for the second, `_2_` for the third, and so on).\n\n> **💡 Tip**\n>\n> Set `libreOfficeSessionLimit` to match your endpoint count so the pool uses all of them concurrently. With 3 endpoints and a session limit of 1, you'll only ever use one at a time.\n\n\n#### Endpoint Configuration\n\nThe `host` field accepts a Docker service name (e.g. `unoserver1`), a DNS hostname (e.g. `uno.internal.example.com`), or an IP address (e.g. `192.168.1.50`). The default is `127.0.0.1`.\n\nThe `hostLocation` setting controls how files are transferred between Stirling PDF and the UNO server:\n\n| Value | When to Use | How It Works |\n|---|---|---|\n| `auto` | Default, detects automatically | Checks if the host is local or remote |\n| `local` | UNO server is on the same machine | Files are passed via filesystem paths (fastest) |\n| `remote` | UNO server is a separate container or machine | Files are transferred over HTTP |\n\n> **⚠️ Caution**\n>\n> Use `remote` when running UNO servers in separate Docker containers, even if the containers are on the same host machine. The containers don't share a filesystem, so `local` will not work.\n\n\n---\n\n## The `stirling-unoserver` Image\n\n`ghcr.io/stirling-tools/stirling-unoserver` is the official standalone worker image.\n\n> **⚠️ Caution: Alpha release**\n>\n> The `stirling-unoserver` image is currently in **alpha**. Only the `:alpha` tag is published today. `:latest` and versioned tags (`:1.0.0`, `:1.0.1`, etc.) will follow once we cut the first stable release. The compose examples on this page reference `:latest` for forward-compatibility, so for now substitute `:alpha` until the stable release is announced. Configuration variables and behaviour are not expected to change between alpha and 1.0.\n\n\n| Tag | Status | Use |\n|---|---|---|\n| `:alpha` | **Available now** | All deployments while the image is in alpha |\n| `:latest` | Coming soon | Production once 1.0 ships |\n| `:1.0.0`, `:1.0.1`, etc. | Coming soon | Pinned version once 1.0 ships |\n\nThe latest `stirling-unoserver` image is always compatible with the latest Stirling PDF, so if you track `:latest` (or `:alpha` today) on both you don't need to coordinate upgrades. The image will be versioned independently from Stirling PDF, so you can also pin a specific version and update it on its own cadence. Any compatibility breaks will be called out in release notes.\n\n### Configuration\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `UNOSERVER_PORT` | `2003` | Listen port. |\n| `UNOSERVER_INTERFACE` | `0.0.0.0` | Listen address; use `127.0.0.1` to restrict to the same host. |\n| `UNOSERVER_CONVERSION_TIMEOUT` | `1800` (s) | Max time per conversion. Set ≥ `libreOfficeTimeoutMinutes`. |\n| `UNOSERVER_RECYCLE_INTERVAL_SECONDS` | `0` (off) | Periodic restart to bound LibreOffice memory growth. Minimum 60 s; e.g. `3600` for hourly. |\n\n### CPU allocation\n\nCore allocation between instances should be handled automatically by the Linux scheduler. However, if you see uneven core usage or want to cap how much CPU each worker can use, you have two options:\n\n**Soft cap (recommended).** Limit each container to a CPU budget. The kernel still picks which cores to use.\n\n```yaml\nservices:\n unoserver1:\n image: ghcr.io/stirling-tools/stirling-unoserver:alpha\n deploy:\n resources:\n limits:\n cpus: \"2.0\" # up to 2 cores worth of CPU time\n memory: 1g\n```\n\nFor `docker run`: `--cpus=\"2.0\"`.\n\n**Hard pinning.** Bind each container to specific cores. Use this only if the soft cap isn't enough.\n\n```yaml\nservices:\n unoserver1:\n image: ghcr.io/stirling-tools/stirling-unoserver:alpha\n cpuset: \"0,1\"\n unoserver2:\n image: ghcr.io/stirling-tools/stirling-unoserver:alpha\n cpuset: \"2,3\"\n```\n\nFor `docker run`: `--cpuset-cpus=\"0,1\"`. For systemd-managed unoservers, the equivalent is `CPUAffinity=0 1` in the service unit.\n\n### CJK fonts\n\nThe default image covers European languages with hyphenation for EN/FR/DE/ES/IT/PT/NL/PL/RU. For Chinese/Japanese/Korean, rebuild with `--build-arg INSTALL_CJK_FONTS=true` (~120 MB extra).\n\n---\n\n## Running UNO Servers Without Docker\n\nIf you are running Stirling PDF without Docker (bare metal or systemd), you can start additional UNO server instances manually using the `unoserver` Python package:\n\n```bash\n# Install unoserver (included in Docker images)\npip install unoserver\n\n# Start instances on different ports\nunoserver --port 2003 &\nunoserver --port 2004 &\nunoserver --port 2005 &\n```\n\nThen configure Stirling PDF to connect to these instances at `127.0.0.1` on the respective ports with `hostLocation: \"local\"`.\n\n---\n\n## Timeout Configuration\n\nLibreOffice conversion has a default timeout of **30 minutes**. For very large or complex documents, you may need to increase this:\n\n\n \n ```yaml\n processExecutor:\n timeoutMinutes:\n libreOfficetimeoutMinutes: 60\n ```\n \n \n ```bash\n PROCESS_EXECUTOR_TIMEOUT_MINUTES_LIBRE_OFFICETIMEOUT_MINUTES=60\n ```\n \n\n\nIf conversions are consistently timing out, this usually indicates the system is under-resourced rather than needing a longer timeout. Check CPU and memory usage first.\n\n---\n\n## Host resource requirements\n\n- **Memory** - ~70 MB idle, 140–250 MB during conversion, per worker. Add headroom for the OS and Stirling PDF itself.\n- **CPU** - each active conversion saturates roughly one CPU core (LibreOffice is single-threaded per document). Start with one worker per two cores; the kernel handles core distribution automatically. See [CPU allocation](#cpu-allocation) if you want to cap or pin workers explicitly.\n\n---\n\n## Related\n\n- [Process Limits](doc:configuration/process-limits) - Configure session limits and timeouts for all external tools\n- [Production Deployment Guide](doc:server-admin-onboarding) - Sizing recommendations for different workloads\n- [Diagnostics](doc:configuration/diagnostics) - Collect system and application diagnostics for troubleshooting", - "sourcePath": "docs/Configuration/LibreOffice-Parallel-Processing.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/LibreOffice-Parallel-Processing.md" - }, - "configuration/mobile-scanner": { - "id": "configuration/mobile-scanner", + "configuration/customisation/mobile-scanner": { + "id": "configuration/customisation/mobile-scanner", "title": "Mobile Scanner Configuration", "description": "Enable and configure Mobile Scanner for document scanning via phone camera", - "section": "configuration", - "markdown": "Enable and configure the Mobile Scanner feature, which lets users scan documents with their phone camera and upload them directly to Stirling PDF via QR code.\n\n## Settings\n\n\n \n ```yaml\n system:\n enableMobileScanner: true\n mobileScannerSettings:\n convertToPdf: true # Convert images to PDF (true/false)\n imageResolution: full # 'full' (original size) or 'reduced' (max 1200px)\n pageFormat: A4 # 'keep' (original dimensions), 'A4', or 'letter'\n stretchToFit: false # Stretch images to fill page (may distort)\n ```\n \n \n ```bash\n SYSTEM_ENABLEMOBILESCANNER=true\n SYSTEM_MOBILESCANNERSETTINGS_CONVERTTOPDF=true\n SYSTEM_MOBILESCANNERSETTINGS_IMAGERESOLUTION=full\n SYSTEM_MOBILESCANNERSETTINGS_PAGEFORMAT=A4\n SYSTEM_MOBILESCANNERSETTINGS_STRETCHTOFIT=false\n ```\n \n \n ```bash\n docker run -d \\\n -p 8080:8080 \\\n -e SYSTEM_ENABLEMOBILESCANNER=true \\\n -e SYSTEM_MOBILESCANNERSETTINGS_CONVERTTOPDF=true \\\n -e SYSTEM_MOBILESCANNERSETTINGS_IMAGERESOLUTION=full \\\n -e SYSTEM_MOBILESCANNERSETTINGS_PAGEFORMAT=A4 \\\n -e SYSTEM_MOBILESCANNERSETTINGS_STRETCHTOFIT=false \\\n stirlingtools/stirling-pdf:latest\n ```\n \n \n ```yaml\n environment:\n SYSTEM_ENABLEMOBILESCANNER: true\n SYSTEM_MOBILESCANNERSETTINGS_CONVERTTOPDF: true\n SYSTEM_MOBILESCANNERSETTINGS_IMAGERESOLUTION: full\n SYSTEM_MOBILESCANNERSETTINGS_PAGEFORMAT: A4\n SYSTEM_MOBILESCANNERSETTINGS_STRETCHTOFIT: false\n ```\n \n\n\n## Configuration Options\n\n| Setting | Values | Description |\n|---------|--------|-------------|\n| `enableMobileScanner` | `true` / `false` | Enable/disable Mobile Scanner feature |\n| `convertToPdf` | `true` / `false` | Automatically convert uploaded images to PDF. If false, images are kept as-is. |\n| `imageResolution` | `full` / `reduced` | Image resolution for PDF conversion: `full` = original size, `reduced` = max 1200px on longest side. Only applies when `convertToPdf` is true. |\n| `pageFormat` | `keep` / `A4` / `letter` | Page format for converted PDFs: `keep` = original image dimensions, `A4` = A4 page size, `letter` = US Letter page size. Only applies when `convertToPdf` is true. |\n| `stretchToFit` | `true` / `false` | Stretch images to fill entire page (may distort aspect ratio). If false, images are centered with preserved aspect ratio. Only applies when `convertToPdf` is true. |\n\n## Desktop app behaviour\n\nThe Stirling PDF desktop app has built-in support for Mobile Scanner. For the end-user walkthrough, see [Mobile Scanner](doc:functionality/mobile-scanner).\n\nAdmin note specific to the desktop app:\n\n- In desktop mode the app serves its own simple upload page (controlled by the `STIRLING_PDF_TAURI_MODE` setting) instead of the standard web `/mobile-scanner` page, because a phone cannot load the desktop app's own window.", - "sourcePath": "docs/Configuration/Mobile-Scanner.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Mobile-Scanner.md" + "section": "configuration/customisation", + "markdown": "Enable and configure the Mobile Scanner feature, which lets users scan documents with their phone camera and upload them directly to Stirling PDF via QR code.\n\n## Settings\n\n\n \n ```yaml\n system:\n enableMobileScanner: true\n mobileScannerSettings:\n convertToPdf: true # Convert images to PDF (true/false)\n imageResolution: full # 'full' (original size) or 'reduced' (max 1200px)\n pageFormat: A4 # 'keep' (original dimensions), 'A4', or 'letter'\n stretchToFit: false # Stretch images to fill page (may distort)\n ```\n \n \n ```bash\n SYSTEM_ENABLEMOBILESCANNER=true\n SYSTEM_MOBILESCANNERSETTINGS_CONVERTTOPDF=true\n SYSTEM_MOBILESCANNERSETTINGS_IMAGERESOLUTION=full\n SYSTEM_MOBILESCANNERSETTINGS_PAGEFORMAT=A4\n SYSTEM_MOBILESCANNERSETTINGS_STRETCHTOFIT=false\n ```\n \n \n ```bash\n docker run -d \\\n -p 8080:8080 \\\n -e SYSTEM_ENABLEMOBILESCANNER=true \\\n -e SYSTEM_MOBILESCANNERSETTINGS_CONVERTTOPDF=true \\\n -e SYSTEM_MOBILESCANNERSETTINGS_IMAGERESOLUTION=full \\\n -e SYSTEM_MOBILESCANNERSETTINGS_PAGEFORMAT=A4 \\\n -e SYSTEM_MOBILESCANNERSETTINGS_STRETCHTOFIT=false \\\n docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n ```\n \n \n ```yaml\n environment:\n SYSTEM_ENABLEMOBILESCANNER: true\n SYSTEM_MOBILESCANNERSETTINGS_CONVERTTOPDF: true\n SYSTEM_MOBILESCANNERSETTINGS_IMAGERESOLUTION: full\n SYSTEM_MOBILESCANNERSETTINGS_PAGEFORMAT: A4\n SYSTEM_MOBILESCANNERSETTINGS_STRETCHTOFIT: false\n ```\n \n\n\n## Configuration Options\n\n| Setting | Values | Description |\n|---------|--------|-------------|\n| `enableMobileScanner` | `true` / `false` | Enable/disable Mobile Scanner feature |\n| `convertToPdf` | `true` / `false` | Automatically convert uploaded images to PDF. If false, images are kept as-is. |\n| `imageResolution` | `full` / `reduced` | Image resolution for PDF conversion: `full` = original size, `reduced` = max 1200px on longest side. Only applies when `convertToPdf` is true. |\n| `pageFormat` | `keep` / `A4` / `letter` | Page format for converted PDFs: `keep` = original image dimensions, `A4` = A4 page size, `letter` = US Letter page size. Only applies when `convertToPdf` is true. |\n| `stretchToFit` | `true` / `false` | Stretch images to fill entire page (may distort aspect ratio). If false, images are centered with preserved aspect ratio. Only applies when `convertToPdf` is true. |\n\n## Desktop app behaviour\n\nThe Stirling PDF desktop app has built-in support for Mobile Scanner. For the end-user walkthrough, see [Mobile Scanner](doc:functionality/mobile-scanner).\n\nAdmin note specific to the desktop app:\n\n- In desktop mode the app serves its own simple upload page (controlled by the `STIRLING_PDF_TAURI_MODE` setting) instead of the standard web `/mobile-scanner` page, because a phone cannot load the desktop app's own window.", + "sourcePath": "docs/Configuration/Customisation/Mobile-Scanner.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Customisation/Mobile-Scanner.md" }, - "configuration/oauth-sso-configuration": { - "id": "configuration/oauth-sso-configuration", - "title": "OAuth 2.0 Single Sign-On Configuration", - "section": "configuration", - "markdown": "> **Tier**: Server\n\nStirling PDF supports Single Sign-On (SSO) using OAuth 2.0 OpenID Connect (OIDC). This allows users to log in using accounts from external providers such as Google, GitHub, Keycloak, Authentik, and others.\n\n> **Looking for SAML 2.0 SSO?** See [SAML SSO Configuration](doc:configuration/saml-sso-configuration/saml-sso-configuration) (Enterprise tier).\n\n## Prerequisites\n\nBefore configuring OAuth 2.0 SSO, ensure you have:\n\n- [ ] Stirling PDF with login enabled (`security.enableLogin: true`)\n- [ ] Valid license for the Server tier or higher\n- [ ] An OAuth 2.0 provider account (Google, GitHub, Keycloak, etc.)\n- [ ] Registered OAuth application with your provider\n- [ ] OAuth Client ID and Client Secret from your provider\n- [ ] Public HTTPS URL configured with `system.backendUrl` set to your public backend API URL (often same as frontend, verify `https://your-domain.com/api/v1/info/status` is accessible)\n- [ ] Callback URL added to provider: `https://your-domain.com/login/oauth2/code/`\n\n> **Tip**: Start with `loginMethod: all` during initial setup to allow both username/password and OAuth login. This ensures you can always access the admin account if SSO configuration needs adjustment.\n\n## Setup Guide\n\n### Step 1: Configure Login Settings\n\nEnable login and set the login method to allow both standard and OAuth authentication during initial setup.\n\n\n \n ```yaml\n security:\n enableLogin: true\n loginMethod: all # Allows both username/password and OAuth login\n ```\n \n \n ```bash\n SECURITY_ENABLELOGIN=true\n SECURITY_LOGINMETHOD=all\n ```\n \n\n\n**Login Method Options:**\n- `all`: Enables all login methods (username/password + OAuth 2)\n- `normal`: Username/password only\n- `oauth2`: OAuth 2 SSO only (disables username/password login)\n- `saml2`: SAML 2 SSO only (Enterprise tier)\n\n### Step 2: Create Initial Admin Account\n\nBefore enabling OAuth, create an initial admin account using one of these methods:\n\n**Option A: Use initialLogin credentials** (recommended for first setup)\n\n\n \n ```yaml\n security:\n initialLogin:\n username: 'admin'\n password: 'yourSecurePassword123'\n ```\n \n \n ```bash\n SECURITY_INITIALLOGIN_USERNAME=admin\n SECURITY_INITIALLOGIN_PASSWORD=yourSecurePassword123\n ```\n \n\n\n**Option B: Create admin manually**\n1. Access Stirling PDF with OAuth disabled\n2. Create an admin user through the UI\n3. Then enable OAuth\n\n### Step 3: Configure OAuth Provider\n\nSet `security.oauth2.enabled` to `true` and configure your chosen provider.\n\n\n \n \n \n ```yaml\n security:\n oauth2:\n enabled: true\n client:\n google:\n clientId: \n clientSecret: \n scopes: email, profile\n useAsUsername: email\n provider: google\n autoCreateUser: true\n blockRegistration: false\n ```\n \n \n ```bash\n SECURITY_OAUTH2_ENABLED=true\n SECURITY_OAUTH2_CLIENT_GOOGLE_CLIENTID=\n SECURITY_OAUTH2_CLIENT_GOOGLE_CLIENTSECRET=\n SECURITY_OAUTH2_CLIENT_GOOGLE_SCOPES=email, profile\n SECURITY_OAUTH2_CLIENT_GOOGLE_USEASUSERNAME=email\n SECURITY_OAUTH2_PROVIDER=google\n SECURITY_OAUTH2_AUTOCREATEUSER=true\n SECURITY_OAUTH2_BLOCKREGISTRATION=false\n ```\n \n \n\n **Provider Setup:**\n 1. Go to [Google Cloud Console](https://console.cloud.google.com/)\n 2. Create a new project or select existing\n 3. Enable Google+ API\n 4. Create OAuth 2.0 credentials (Web application)\n 5. Add authorized redirect URI: `https://your-domain.com/login/oauth2/code/google`\n 6. Copy Client ID and Client Secret\n \n \n \n \n ```yaml\n security:\n oauth2:\n enabled: true\n client:\n github:\n clientId: \n clientSecret: \n scopes: read:user\n useAsUsername: login\n provider: github\n autoCreateUser: true\n blockRegistration: false\n ```\n \n \n ```bash\n SECURITY_OAUTH2_ENABLED=true\n SECURITY_OAUTH2_CLIENT_GITHUB_CLIENTID=\n SECURITY_OAUTH2_CLIENT_GITHUB_CLIENTSECRET=\n SECURITY_OAUTH2_CLIENT_GITHUB_SCOPES=read:user\n SECURITY_OAUTH2_CLIENT_GITHUB_USEASUSERNAME=login\n SECURITY_OAUTH2_PROVIDER=github\n SECURITY_OAUTH2_AUTOCREATEUSER=true\n SECURITY_OAUTH2_BLOCKREGISTRATION=false\n ```\n \n \n\n **Provider Setup:**\n 1. Go to [GitHub Developer Settings](https://github.com/settings/developers)\n 2. Create new OAuth App\n 3. Set Authorization callback URL: `https://your-domain.com/login/oauth2/code/github`\n 4. Copy Client ID and generate Client Secret\n \n \n \n \n ```yaml\n security:\n oauth2:\n enabled: true\n issuer: https://your-keycloak.com/realms/your-realm\n clientId: \n clientSecret: \n scopes: openid, profile, email\n useAsUsername: preferred_username\n provider: keycloak\n autoCreateUser: true\n blockRegistration: false\n ```\n \n \n ```bash\n SECURITY_OAUTH2_ENABLED=true\n SECURITY_OAUTH2_ISSUER=https://your-keycloak.com/realms/your-realm\n SECURITY_OAUTH2_CLIENTID=\n SECURITY_OAUTH2_CLIENTSECRET=\n SECURITY_OAUTH2_SCOPES=openid, profile, email\n SECURITY_OAUTH2_USEASUSERNAME=preferred_username\n SECURITY_OAUTH2_PROVIDER=keycloak\n SECURITY_OAUTH2_AUTOCREATEUSER=true\n SECURITY_OAUTH2_BLOCKREGISTRATION=false\n ```\n \n \n\n **Provider Setup:**\n 1. Access your Keycloak admin console\n 2. Select your realm\n 3. Create new client (OpenID Connect)\n 4. Set Valid Redirect URIs: `https://your-domain.com/login/oauth2/code/keycloak`\n 5. Enable \"Client authentication\" for confidential access\n 6. Copy Client ID and Client Secret from Credentials tab\n \n \n \n \n ```yaml\n security:\n oauth2:\n enabled: true\n issuer: https://your-authentik.com/application/o/stirling-pdf/\n clientId: \n clientSecret: \n scopes: openid, profile, email\n useAsUsername: preferred_username\n provider: authentik\n autoCreateUser: true\n blockRegistration: false\n ```\n \n \n ```bash\n SECURITY_OAUTH2_ENABLED=true\n SECURITY_OAUTH2_ISSUER=https://your-authentik.com/application/o/stirling-pdf/\n SECURITY_OAUTH2_CLIENTID=\n SECURITY_OAUTH2_CLIENTSECRET=\n SECURITY_OAUTH2_SCOPES=openid, profile, email\n SECURITY_OAUTH2_USEASUSERNAME=preferred_username\n SECURITY_OAUTH2_PROVIDER=authentik\n SECURITY_OAUTH2_AUTOCREATEUSER=true\n SECURITY_OAUTH2_BLOCKREGISTRATION=false\n ```\n \n \n\n **Provider Setup:**\n 1. Create new Provider (OAuth2/OpenID)\n 2. Create new Application\n 3. Set Redirect URIs: `https://your-domain.com/login/oauth2/code/authentik`\n 4. Copy Client ID and Client Secret\n \n \n \n \n ```yaml\n security:\n oauth2:\n enabled: true\n issuer: \n clientId: \n clientSecret: \n scopes: openid, profile, email\n useAsUsername: email\n provider: \n autoCreateUser: true\n blockRegistration: false\n ```\n \n \n ```bash\n SECURITY_OAUTH2_ENABLED=true\n SECURITY_OAUTH2_ISSUER=\n SECURITY_OAUTH2_CLIENTID=\n SECURITY_OAUTH2_CLIENTSECRET=\n SECURITY_OAUTH2_SCOPES=openid, profile, email\n SECURITY_OAUTH2_USEASUSERNAME=email\n SECURITY_OAUTH2_PROVIDER=\n SECURITY_OAUTH2_AUTOCREATEUSER=true\n SECURITY_OAUTH2_BLOCKREGISTRATION=false\n ```\n \n \n\n **Requirements:**\n - Provider must support OpenID Connect Discovery\n - Must expose `/.well-known/openid-configuration` endpoint\n \n\n\n### Step 4: Configure Callback URL\n\nWhen registering your application with the OAuth provider, use this callback URL format:\n\n```\nhttps:///login/oauth2/code/\n```\n\n**Understanding the Provider Slug:**\n\nThe `` portion of the callback URL must exactly match your `security.oauth2.provider` configuration value:\n\n```yaml\nsecurity:\n oauth2:\n provider: authentik # This becomes part of the callback URL\n```\n\nWith the above configuration, your callback URL becomes:\n```\nhttps://your-domain.com/login/oauth2/code/authentik\n```\n\n**Examples:**\n- Google: `https://stirling.example.com/login/oauth2/code/google`\n- GitHub: `https://stirling.example.com/login/oauth2/code/github`\n- Keycloak: `https://stirling.example.com/login/oauth2/code/keycloak`\n- Custom provider: `https://stirling.example.com/login/oauth2/code/mycompany`\n\n> **Important**: If the provider slug in the callback URL doesn't match your `security.oauth2.provider` value, OAuth login will fail with redirect errors.\n\n> **Tip**: For generic OIDC providers (not Google/GitHub/Keycloak), you can set `provider` to any lowercase alphanumeric value that makes sense for your organization.\n\n### Step 5: Test OAuth Login and Promote User\n\n1. Restart Stirling PDF\n2. Test OAuth login in an incognito/private browser window\n3. Verify you can log in with your OAuth provider\n4. Log in with your initial admin account (username/password)\n5. Go to **Settings** → **User Management**\n6. Find the OAuth user account (created during test login)\n7. Change role to **Admin**\n\n### Step 6: (Optional) Switch to SSO-Only Mode\n\nOnce you've verified OAuth works and promoted an OAuth user to admin, you can disable username/password login:\n\n\n \n ```yaml\n security:\n loginMethod: oauth2 # Disables username/password login\n ```\n \n \n ```bash\n SECURITY_LOGINMETHOD=oauth2\n ```\n \n\n\n> **Important**: If you set `loginMethod: oauth2` before creating an OAuth admin user, you will only be able to log in via OAuth, and all new OAuth users will have regular user permissions. Keep `loginMethod: all` until you have at least one OAuth user with admin privileges.\n\n## Configuration Reference\n\n### Required Properties\n\n| Property | Description | Example |\n|----------|-------------|---------|\n| `security.oauth2.enabled` | Enable OAuth 2 login | `true` |\n| `security.oauth2.clientId` | Client ID from your OAuth provider | `stirling-pdf-client` |\n| `security.oauth2.clientSecret` | Client Secret from your OAuth provider | `your-secret-key` |\n| `security.oauth2.provider` | Provider name | `google`, `github`, `keycloak`, `authentik` |\n\n### Optional Properties\n\n| Property | Description | Default | Example |\n|----------|-------------|---------|---------|\n| `security.oauth2.issuer` | OIDC issuer URL (required for generic providers, must support `/.well-known/openid-configuration`) | - | `https://keycloak.example.com/realms/myrealm` |\n| `security.oauth2.autoCreateUser` | Auto-create users on first login | `true` | `false` |\n| `security.oauth2.blockRegistration` | Block new user registration, only allow pre-registered users | `false` | `true` |\n| `security.oauth2.scopes` | Space or comma-separated list of OAuth scopes | Provider-specific | `openid, profile, email` |\n| `security.oauth2.useAsUsername` | Claim to use as username (options depend on provider) | Provider-specific | `email`, `preferred_username`, `login` |\n\n### Provider-Specific Configuration\n\n**Named providers** (Google, GitHub, Keycloak):\n```yaml\noauth2:\n client:\n google: # or github, keycloak\n clientId: ...\n clientSecret: ...\n```\n\n**Generic providers** (Authentik, custom OIDC):\n```yaml\noauth2:\n issuer: # Must support OIDC discovery\n clientId: ...\n clientSecret: ...\n```\n\n### Username Claim Options\n\n**Google:**\n- `email`, `name`, `given_name`, `family_name`\n- See [Google OAuth Scopes](https://developers.google.com/identity/protocols/oauth2/scopes)\n\n**GitHub:**\n- `login`, `email`, `name`\n- See [GitHub OAuth Scopes](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps)\n\n**Keycloak/Generic OIDC:**\n- `email`, `preferred_username`, `nickname`, `name`\n\n## Advanced Configuration\n\n### Backend URL Configuration\n\nIf your Stirling PDF backend is accessible at a different URL than the frontend, configure the backend URL:\n\n\n \n ```yaml\n system:\n backendUrl: https://stirling-api.example.com\n ```\n \n \n ```bash\n SYSTEM_BACKENDURL=https://stirling-api.example.com\n ```\n \n\n\nVerify the backend URL is correct by checking that `https://your-domain.com/api/v1/info/status` is accessible.\n\n### Auto-Login Feature\n> **Tier**: Server\n\nAutomatically redirect users to OAuth login page, bypassing the Stirling PDF login screen.\n\n\n \n ```yaml\n premium:\n proFeatures:\n ssoAutoLogin: true\n ```\n \n \n ```bash\n PREMIUM_PROFEATURES_SSOAUTOLOGIN=true\n ```\n \n\n\n**Auto-login Activation Requirements:**\n\nAuto-login only triggers when **ALL** of the following conditions are met:\n\n1. `ssoAutoLogin` is enabled (as configured above)\n2. `loginMethod` is NOT `'all'` and NOT `'normal'` (i.e., SSO-only mode required)\n3. Exactly one OAuth provider is configured\n\n**Behavior:**\n- When all conditions are met: Users are automatically redirected to OAuth provider login\n- When conditions are not met: Standard login page is displayed\n- If the SSO redirect fails, the browser stops auto-redirecting for the current session so the login page stays reachable\n- After logging out, auto-redirect is suppressed for that session so you can sign in as a different user\n\n### User Interface\n\nOnce OAuth is configured, users will see the SSO login button:\n\n| ![login-page.png](https://raw.githubusercontent.com/Stirling-Tools/Stirling-Tools.github.io/main/static/img/login-page.png) | ![sso-login-option.png](https://raw.githubusercontent.com/Stirling-Tools/Stirling-Tools.github.io/main/static/img/sso-login-option.png) |\n|----------------------------------------|---------------------------------------------------|\n\n## Troubleshooting\n\n### Common Issues\n\n**\"OAuth2 authentication error\"**\n- Verify callback URL matches exactly (including provider slug)\n- Check client ID and secret are correct\n- Ensure provider allows the configured redirect URI\n- Confirm `security.oauth2.provider` matches the provider slug in callback URL\n\n**\"Invalid issuer\"**\n- Confirm issuer URL is correct\n- Test `https://your-issuer/.well-known/openid-configuration` returns valid JSON\n- Check network connectivity from Stirling PDF container to provider\n\n**\"User not created\"**\n- Set `autoCreateUser: true`\n- Check `blockRegistration` is `false` or user is pre-registered\n- Verify license allows user count\n\n**Users redirected to wrong URL**\n- Verify `system.backendUrl` is configured correctly\n- Test that `https://your-domain.com/api/v1/info/status` is accessible\n- Check provider's registered redirect URIs match your domain\n\n### Debug Logging\n\nEnable OAuth debug logging to troubleshoot authentication issues.\n\n\n \n ```yaml\n logging:\n level:\n org.springframework.security.oauth2: DEBUG\n ```\n \n \n ```bash\n LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_SECURITY_OAUTH2=DEBUG\n ```\n \n\n\n### Logging the Provider's Claims (\"Attribute value for email cannot be null\")\n\nIf login fails with **\"Attribute value for email cannot be null\"** (common with ADFS and Azure AD), the provider is not returning the claim named by `useAsUsername`. Enable `security.oauth2.debugLogging` to log the full ID-token / UserInfo claim set and the resolved username, so you can see exactly which claims the provider sends and pick the right `useAsUsername` value.\n\n\n \n ```yaml\n security:\n oauth2:\n debugLogging: true\n ```\n \n \n ```bash\n SECURITY_OAUTH2_DEBUGLOGGING=true\n ```\n \n\n\nThe claims are logged at `INFO` level on each login (and again at `ERROR` level when the username attribute cannot be resolved).\n\n**⚠️ Disable `debugLogging` again as soon as you are done.** It writes personally identifiable information (such as `sub`, `email`, and `name`) to the application logs.\n\n## Known Limitations\n\n- OAuth users must be manually promoted to admin role after first login\n- Provider discovery requires `/.well-known/openid-configuration` endpoint support\n- Auto-login feature requires the Server tier (or higher)\n\n## See Also\n\n- [SAML SSO Configuration](doc:configuration/saml-sso-configuration/saml-sso-configuration) - Enterprise SAML 2.0 setup\n- [System and Security](doc:configuration/system-and-security) - Additional security settings\n- [External Database](doc:configuration/external-database) - User storage configuration", - "sourcePath": "docs/Configuration/OAuth SSO Configuration.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/OAuth SSO Configuration.md" - }, - "configuration/ocr": { - "id": "configuration/ocr", - "title": "OCR (Optical Character Recognition)", - "section": "configuration", - "markdown": "## OCR Language Packs and Setup\nThis document provides instructions on how to add additional language packs for the OCR tab in Stirling PDF, both inside and outside of Docker.\n\n## How does the OCR Work\nStirling PDF uses Tesseract for its text recognition. All credit goes to them for this awesome work! Note that OCR recognizes text only - it does not perform table-structure or formula recognition.\n\n> **📝 Note: Requires a server backend**\n>\n> OCR runs on a server-side backend with Tesseract installed. The desktop app cannot OCR in local-only mode - connect it to Stirling Cloud or a self-hosted server that has OCR available. To OCR non-English documents, install the matching language pack as described below.\n\n\n## Language Packs\n\nTesseract OCR supports a variety of languages. You can find additional language packs in the Tesseract GitHub repositories:\n\n- [tessdata_fast](https://github.com/tesseract-ocr/tessdata_fast): These language packs are smaller and faster to load but may provide lower recognition accuracy.\n- [tessdata](https://github.com/tesseract-ocr/tessdata): These language packs are larger and provide better recognition accuracy, but may take longer to load.\n\nDepending on your requirements, you can choose the appropriate language pack for your use case. By default, Stirling PDF uses `tessdata_fast` for English, but this can be replaced.\n\n### Installing Language Packs manually\n\n1. Download the desired language pack(s) by selecting the `.traineddata` file(s) for the language(s) you need.\n2. Place the `.traineddata` files in the Tesseract tessdata directory: `/usr/share/tessdata` (or equivalent)\n\n**DO NOT REMOVE EXISTING `eng.traineddata`, IT'S REQUIRED.**\n\n### Docker Setup\n\nIf you are using Docker, you need to expose the Tesseract tessdata directory as a volume in order to use the additional language packs.\n\n\n \n Modify your `docker-compose.yml` file to include the following volume configuration:\n\n ```yaml\n services:\n your_service_name:\n image: your_docker_image_name\n volumes:\n - /location/of/trainingData:/usr/share/tessdata\n ```\n \n \n Add the following to your existing Docker run command:\n\n ```bash\n -v /location/of/trainingData:/usr/share/tessdata\n ```\n \n\n\n### Non-Docker Setup\n\n\n \n For Debian-based systems, use the following commands to manage Tesseract languages:\n\n ```bash\n sudo apt update &&\\\n # All languages\n # sudo apt install -y 'tesseract-ocr-*'\n \n # Find available languages:\n apt search tesseract-ocr-\n \n # View installed languages:\n dpkg-query -W tesseract-ocr- | sed 's/tesseract-ocr-//g'\n ```\n \n \n For Fedora systems, use the following commands:\n\n ```bash\n # All languages\n # sudo dnf install -y tesseract-langpack-*\n \n # Find available languages:\n dnf search -C tesseract-langpack-\n \n # View installed languages:\n rpm -qa | grep tesseract-langpack | sed 's/tesseract-langpack-//g'\n ```\n \n \n Follow these steps to set up Tesseract languages on Windows:\n\n 1. Download desired `.traineddata` files from [tessdata](https://github.com/tesseract-ocr/tessdata) or [tessdata_fast](https://github.com/tesseract-ocr/tessdata_fast)\n \n 2. Place them in the tessdata folder within your Tesseract installation directory:\n ```\n C:\\Program Files\\Tesseract-OCR\\tessdata\n ```\n \n 3. Verify the installation by running:\n ```powershell\n tesseract --list-langs\n ```\n \n 4. Edit your `/configs/settings.yml` and update the `system.tessdataDir`:\n ```yaml\n system:\n tessdataDir: C:/Program Files/Tesseract-OCR/tessdata # path to Tessdata files\n ```", - "sourcePath": "docs/Configuration/OCR.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/OCR.md" - }, - "configuration/other-customisations": { - "id": "configuration/other-customisations", + "configuration/customisation/other-customisations": { + "id": "configuration/customisation/other-customisations", "title": "Other Customisations", - "section": "configuration", - "markdown": "Stirling PDF offers various other customisation options, such as:\n\n## Static File Overrides\n\nYou can override static files (logos, images, favicons, etc.) by placing custom versions in the `customFiles/static/` directory.\n\n### How It Works\n\nStirling PDF checks for files in this order:\n1. **First:** `customFiles/static/` (your custom files)\n2. **Fallback:** Built-in static files embedded in the application\n\nThis means you can replace any static resource by placing a file with the matching path in `customFiles/static/`.\n\n### Finding File Paths to Override\n\nMost static files in the application come from the `frontend/editor/public/` folder in the source code (brand logos live in `frontend/shared/assets/brand/`). To override a file, place it under `customFiles/static/` matching the same path it is served at. The mapping is direct:\n\n**Frontend source → Your override path:**\n- `frontend/editor/public/manifest.json` → `customFiles/static/manifest.json`\n- `frontend/shared/assets/brand/modern-logo/StirlingPDFLogoBlackText.svg` → `customFiles/static/modern-logo/StirlingPDFLogoBlackText.svg`\n- `frontend/shared/assets/brand/classic-logo/StirlingPDFLogoBlackText.svg` → `customFiles/static/classic-logo/StirlingPDFLogoBlackText.svg`\n\n**To see what files you can override:**\n1. Browse the [frontend/editor/public folder on GitHub](https://github.com/Stirling-Tools/Stirling-PDF/tree/main/frontend/editor/public)\n2. Match the directory structure in your `customFiles/static/` folder\n\nCommon files you might want to override:\n\n**Favicons & Icons** (override by placing a matching file at the root of `customFiles/static/`):\n- `favicon.svg`, `favicon.ico` - Browser favicons\n- `apple-touch-icon.png` - iOS home screen icon\n- `android-chrome-192x192.png` - Android icon (192x192)\n- `android-chrome-512x512.png` - Android icon (512x512)\n\n**Logo Variants (Both classic-logo/ and modern-logo/):**\n\nBoth logo directories contain the same file structure - just replace `classic-logo/` or `modern-logo/` with whichever style you're using:\n\n- `{style}/StirlingPDFLogoBlackText.svg` - Logo with black text (light mode)\n- `{style}/StirlingPDFLogoWhiteText.svg` - Logo with white text (dark mode)\n- `{style}/StirlingPDFLogoGreyText.svg` - Logo with grey text\n- `{style}/StirlingPDFLogoNoTextDark.svg` - Logo without text (dark variant)\n- `{style}/StirlingPDFLogoNoTextLight.svg` - Logo without text (light variant)\n- `{style}/logo-tooltip.svg` - Small logo for tooltips\n- `{style}/favicon.ico` - Style-specific favicon\n- `{style}/logo192.png`, `{style}/logo512.png` - PNG versions at different sizes\n- `{style}/Firstpage.png` - First page preview image\n\nWhere `{style}` is either `classic-logo` or `modern-logo` depending on your logo style setting:\n\n**Settings file (configs/settings.yml):**\n```yaml\nui:\n logoStyle: classic # Options: 'classic' or 'modern'\n```\n\n**Environment variable (Docker):**\n```bash\nUI_LOGOSTYLE=classic\n```\n\n**In-app configuration:**\nSettings → Configuration → System Settings → Logo Style (requires login enabled)\n\n**Other Assets:**\n- `robots.txt` - Search engine directives\n- `manifest.json`, `manifest-classic.json` - Web app manifests\n- Images, fonts, and locales\n\n### Example: Custom Favicon\n\n```bash\n# Your directory structure\ncustomFiles/\n └── static/\n ├── favicon.svg\n └── favicon.ico\n```\n\nDocker compose:\n```yaml\nvolumes:\n - ./customFiles:/customFiles:rw\n```\n\nRestart the container - your custom favicons will be used!\n\n### Example: Custom Logo (Simple)\n\n```bash\ncustomFiles/\n └── static/\n └── classic-logo/\n └── StirlingPDFLogoBlackText.svg\n```\n\nThis overrides the classic logo with black text (used in light mode).\n\n**Important:** Make sure your logo style is set to `classic` in your configuration:\n```yaml\nui:\n logoStyle: classic # Must match the directory you're overriding!\n```\n\nOr via environment variable:\n```bash\nUI_LOGOSTYLE=classic\n```\n\nIf you have `logoStyle: modern` set, override files in `modern-logo/` instead!\n\n### Example: Complete Branding Customization\n\nTo fully rebrand Stirling PDF with your company logo, override multiple variants:\n\n```bash\ncustomFiles/\n └── static/\n ├── favicon.svg # Main favicon\n ├── favicon.ico # Legacy favicon\n └── classic-logo/ # Or modern-logo/ if using modern style\n ├── StirlingPDFLogoBlackText.svg # Light mode with text\n ├── StirlingPDFLogoWhiteText.svg # Dark mode with text\n ├── StirlingPDFLogoNoTextLight.svg # Light mode icon only\n ├── StirlingPDFLogoNoTextDark.svg # Dark mode icon only\n ├── logo-tooltip.svg # Small icon\n ├── favicon.ico # Style-specific favicon\n └── Firstpage.png # Homepage preview\n```\n\n**Important:** Set your logo style to match the directory:\n```yaml\nui:\n logoStyle: classic # Use 'classic' if overriding classic-logo/, 'modern' if overriding modern-logo/\n```\n\nOr via environment variable: `UI_LOGOSTYLE=classic`\n\n**Tips:**\n- For consistent branding across light/dark modes, provide both:\n - `StirlingPDFLogoBlackText.svg` (shows on light backgrounds)\n - `StirlingPDFLogoWhiteText.svg` (shows on dark backgrounds)\n- You can also configure this in-app: Settings → Configuration → System Settings → Logo Style (if you have login enabled)\n\n### Advanced: Overriding Built Files (HTML, JS, CSS)\n\n**⚠️ For developers only!**\n\nFiles like `index.html`, JavaScript bundles, and CSS are **generated** by the build process from `frontend/editor/src/`. To override these:\n\n1. Clone the Stirling PDF repository\n2. Make your changes to the React source code in `frontend/editor/src/`\n3. Build the frontend: `task frontend:build` (from the repository root)\n4. The built files appear in `frontend/editor/dist/`\n5. Copy the specific files you want to override to `customFiles/static/` matching the path structure\n\n**Example:** To override `index.html`:\n```bash\n# After building the frontend\ncp frontend/editor/dist/index.html customFiles/static/index.html\n```\n\n**Warning:** Built files may include hashed filenames (e.g., `assets/index-abc123.js`) that change with each build. Overriding these requires matching the exact filename from your build and is not recommended for most users.\n\n---\n\n## Defaulting Language\nDefault language selection via the `SYSTEM_DEFAULTLOCALE` environment variable. Accepted values include `de-DE`, `fr-FR`, `ar-AR` and all other languages codes that are within Stirling PDFs current list.\n\n## Google Search Visibility (robots.txt)\nEnable or disable search engine visibility (via `robots.txt`) with the `SYSTEM_GOOGLEVISIBILITY` environment variable, or in `configs/settings.yml`:\n```yaml\nsystem:\n googlevisibility: true # 'true' to allow Google visibility, 'false' to disallow\n```\n\n## Custom Root path\nHost the interface under a sub-path with the `SYSTEM_ROOTURIPATH` environment variable.\nThis is for changing websites like stirlingtools.com to instead host the interface at stirlingtools.com/demo:\n```bash\nSYSTEM_ROOTURIPATH=/demo\n```\nThe setting can also be written in `configs/settings.yml`:\n```yaml\nserver:\n servlet:\n context-path: /demo\n```\n\n## Enable/Disable Analytics\nAnalytics can be enabled/disabled with ``SYSTEM_ENABLEANALYTICS`` or\n```yaml\nsystem:\n enableAnalytics: 'true'\n```\nIn configs/settings.yml", - "sourcePath": "docs/Configuration/Other Customisations.md", - "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Other Customisations.md" + "section": "configuration/customisation", + "markdown": "Stirling PDF offers various other customisation options, such as:\n\n## Static File Overrides\n\nYou can override static files (logos, images, favicons, etc.) by placing custom versions in the `customFiles/static/` directory.\n\n### How It Works\n\nStirling PDF checks for files in this order:\n1. **First:** `customFiles/static/` (your custom files)\n2. **Fallback:** Built-in static files embedded in the application\n\nThis means you can replace any static resource by placing a file with the matching path in `customFiles/static/`.\n\n### Finding File Paths to Override\n\nMost static files in the application come from the `frontend/editor/public/` folder in the source code (brand logos live in `frontend/shared/assets/brand/`). To override a file, place it under `customFiles/static/` matching the same path it is served at. The mapping is direct:\n\n**Frontend source → Your override path:**\n- `frontend/editor/public/manifest.json` → `customFiles/static/manifest.json`\n- `frontend/shared/assets/brand/modern-logo/StirlingPDFLogoBlackText.svg` → `customFiles/static/modern-logo/StirlingPDFLogoBlackText.svg`\n- `frontend/shared/assets/brand/classic-logo/StirlingPDFLogoBlackText.svg` → `customFiles/static/classic-logo/StirlingPDFLogoBlackText.svg`\n\n**To see what files you can override:**\n1. Browse the [frontend/editor/public folder on GitHub](https://github.com/Stirling-Tools/Stirling-PDF/tree/main/frontend/editor/public)\n2. Match the directory structure in your `customFiles/static/` folder\n\nCommon files you might want to override:\n\n**Favicons & Icons** (override by placing a matching file at the root of `customFiles/static/`):\n- `favicon.svg`, `favicon.ico` - Browser favicons\n- `apple-touch-icon.png` - iOS home screen icon\n- `android-chrome-192x192.png` - Android icon (192x192)\n- `android-chrome-512x512.png` - Android icon (512x512)\n\n**Logo Variants (Both classic-logo/ and modern-logo/):**\n\nBoth logo directories contain the same file structure - just replace `classic-logo/` or `modern-logo/` with whichever style you're using:\n\n- `{style}/StirlingPDFLogoBlackText.svg` - Logo with black text (light mode)\n- `{style}/StirlingPDFLogoWhiteText.svg` - Logo with white text (dark mode)\n- `{style}/StirlingPDFLogoGreyText.svg` - Logo with grey text\n- `{style}/StirlingPDFLogoNoTextDark.svg` - Logo without text (dark variant)\n- `{style}/StirlingPDFLogoNoTextLight.svg` - Logo without text (light variant)\n- `{style}/logo-tooltip.svg` - Small logo for tooltips\n- `{style}/favicon.ico` - Style-specific favicon\n- `{style}/logo192.png`, `{style}/logo512.png` - PNG versions at different sizes\n- `{style}/Firstpage.png` - First page preview image\n\nWhere `{style}` is either `classic-logo` or `modern-logo` depending on your logo style setting:\n\n**Settings file (configs/settings.yml):**\n```yaml\nui:\n logoStyle: classic # Options: 'classic' or 'modern'\n```\n\n**Environment variable (Docker):**\n```bash\nUI_LOGOSTYLE=classic\n```\n\n**In-app configuration:**\nSettings → Configuration → System Settings → Logo Style (requires login enabled)\n\n**Other Assets:**\n- `robots.txt` - Search engine directives\n- `manifest.json`, `manifest-classic.json` - Web app manifests\n- Images, fonts, and locales\n\n### Example: Custom Favicon\n\n```bash\n# Your directory structure\ncustomFiles/\n └── static/\n ├── favicon.svg\n └── favicon.ico\n```\n\nDocker compose:\n```yaml\nvolumes:\n - ./customFiles:/customFiles:rw\n```\n\nRestart the container - your custom favicons will be used!\n\n### Example: Custom Logo (Simple)\n\n```bash\ncustomFiles/\n └── static/\n └── classic-logo/\n └── StirlingPDFLogoBlackText.svg\n```\n\nThis overrides the classic logo with black text (used in light mode).\n\n**Important:** Make sure your logo style is set to `classic` in your configuration:\n```yaml\nui:\n logoStyle: classic # Must match the directory you're overriding!\n```\n\nOr via environment variable:\n```bash\nUI_LOGOSTYLE=classic\n```\n\nIf you have `logoStyle: modern` set, override files in `modern-logo/` instead!\n\n### Example: Complete Branding Customization\n\nTo fully rebrand Stirling PDF with your company logo, override multiple variants:\n\n```bash\ncustomFiles/\n └── static/\n ├── favicon.svg # Main favicon\n ├── favicon.ico # Legacy favicon\n └── classic-logo/ # Or modern-logo/ if using modern style\n ├── StirlingPDFLogoBlackText.svg # Light mode with text\n ├── StirlingPDFLogoWhiteText.svg # Dark mode with text\n ├── StirlingPDFLogoNoTextLight.svg # Light mode icon only\n ├── StirlingPDFLogoNoTextDark.svg # Dark mode icon only\n ├── logo-tooltip.svg # Small icon\n ├── favicon.ico # Style-specific favicon\n └── Firstpage.png # Homepage preview\n```\n\n**Important:** Set your logo style to match the directory:\n```yaml\nui:\n logoStyle: classic # Use 'classic' if overriding classic-logo/, 'modern' if overriding modern-logo/\n```\n\nOr via environment variable: `UI_LOGOSTYLE=classic`\n\n**Tips:**\n- For consistent branding across light/dark modes, provide both:\n - `StirlingPDFLogoBlackText.svg` (shows on light backgrounds)\n - `StirlingPDFLogoWhiteText.svg` (shows on dark backgrounds)\n- You can also configure this in-app: Settings → Configuration → System Settings → Logo Style (if you have login enabled)\n\n### Advanced: Overriding Built Files (HTML, JS, CSS)\n\n**⚠️ For developers only!**\n\nFiles like `index.html`, JavaScript bundles, and CSS are **generated** by the build process from `frontend/editor/src/`. To override these:\n\n1. Clone the Stirling PDF repository\n2. Make your changes to the React source code in `frontend/editor/src/`\n3. Build the frontend: `task frontend:build` (from the repository root)\n4. The built files appear in `frontend/editor/dist/`\n5. Copy the specific files you want to override to `customFiles/static/` matching the path structure\n\n**Example:** To override `index.html`:\n```bash\n# After building the frontend\ncp frontend/editor/dist/index.html customFiles/static/index.html\n```\n\n**Warning:** Built files may include hashed filenames (e.g., `assets/index-abc123.js`) that change with each build. Overriding these requires matching the exact filename from your build and is not recommended for most users.\n\n---\n\n## Defaulting Language\nDefault language selection via the `SYSTEM_DEFAULTLOCALE` environment variable. Accepted values include `de-DE`, `fr-FR`, `ar-AR` and all other languages codes that are within Stirling PDFs current list.\n\n## Google Search Visibility (robots.txt)\nEnable or disable search engine visibility (via `robots.txt`) with the `SYSTEM_GOOGLEVISIBILITY` environment variable, or in `configs/settings.yml`:\n```yaml\nsystem:\n googlevisibility: true # 'true' to allow Google visibility, 'false' to disallow\n```\n\n## Custom Root path\nHost the interface under a sub-path with the `SYSTEM_ROOTURIPATH` environment variable.\nThis is for changing websites like stirlingtools.com to instead host the interface at stirlingtools.com/demo:\n```bash\nSYSTEM_ROOTURIPATH=/demo\n```\nThe setting can also be written in `configs/settings.yml`:\n```yaml\nserver:\n servlet:\n context-path: /demo\n```\n\n## Enable/Disable Analytics\nAnalytics can be enabled/disabled with ``SYSTEM_ENABLEANALYTICS`` or\n```yaml\nsystem:\n enableAnalytics: 'true'\n```\nIn configs/settings.yml\n\n## Using an outgoing HTTP(S) proxy\nTo make Stirling PDF use an outgoing proxy server (e.g. for checking the license validity):\n\n\n \n ```bash\n JAVA_CUSTOM_OPTS=\"-Dhttp.proxyHost=proxyserver -Dhttp.proxyPort=8888 -Dhttp.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local' -Dhttps.proxyHost=proxyserver -Dhttps.proxyPort=8888 -Dhttps.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local'\"\n ```\n \n \n ```bash\n docker run -d \\\n -p 8080:8080 \\\n -e JAVA_CUSTOM_OPTS=\"-Dhttp.proxyHost=proxyserver -Dhttp.proxyPort=8888 -Dhttp.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local' -Dhttps.proxyHost=proxyserver -Dhttps.proxyPort=8888 -Dhttps.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local'\" \\\n docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n ```\n \n \n ```yaml\n services:\n stirling-pdf:\n image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n environment:\n JAVA_CUSTOM_OPTS: \"-Dhttp.proxyHost=proxyserver -Dhttp.proxyPort=8888 -Dhttp.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local' -Dhttps.proxyHost=proxyserver -Dhttps.proxyPort=8888 -Dhttps.nonProxyHosts='localhost|127.0.0.1|127.0.1.1|127.0.0.0/8|::1|10.0.0.0/8|.svc|.cluster.local'\"\n ```", + "sourcePath": "docs/Configuration/Customisation/Other Customisations.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Customisation/Other Customisations.md" }, - "configuration/performance-optimization": { - "id": "configuration/performance-optimization", + "configuration/customisation/pdf-to-cbr-conversion": { + "id": "configuration/customisation/pdf-to-cbr-conversion", + "title": "Enabling PDF to CBR Conversion in Stirling PDF", + "section": "configuration/customisation", + "markdown": "## Overview\n\nStirling PDF can convert PDF files into the Comic Book RAR (`.cbr`) format. This process relies on an external command-line utility, `rar`, which is not included by default. To enable this feature, you must first install the `rar` utility on your system and then make it accessible to Stirling PDF.\n\n### What is a CBR file?\n\nA CBR (Comic Book RAR) file is an archive used for distributing digital comic books. It is essentially a collection of sequential image files (e.g., JPEG, PNG) compressed into a single file using RAR compression.\n\nWhile CBR is a popular format, it requires the proprietary `rar` utility for creation. Its more common, open-standard alternative is CBZ (Comic Book ZIP), which is supported by Stirling PDF out of the box.\n\n-----\n\n## Step 1: Install the `rar` Command-Line Utility\n\nThis is a mandatory prerequisite for both Docker and non-Docker setups. The `rar` executable must be installed on the host machine.\n\n### Linux\n\nThe easiest method is to use your distribution's package manager.\n\n**Debian / Ubuntu:**\nThe `rar` package is available in the `non-free` repository.\n\n```bash\nsudo apt update\nsudo apt install rar\n```\n\n**Fedora / CentOS / RHEL:**\nThe `rar` package is available in the RPM Fusion \"non-free\" repository.\n\n```bash\n# First, enable the RPM Fusion non-free repository for your system.\n# See https://rpmfusion.org/Configuration for instructions.\n\n# Then, install rar\nsudo dnf install rar # For Fedora, RHEL 8+, CentOS Stream\n# or\nsudo yum install rar # For CentOS 7\n```\n\n**Manual Installation (Any Linux Distribution):**\n\n1. Visit the official download page: [rarlab.com/download.htm](https://www.rarlab.com/download.htm).\n2. Download the \"RAR for Linux x64\" command-line version.\n3. Extract the archive and install the binary:\n ```bash\n # The version number (e.g., 712, as of writing this guide) will change.\n # Use the actual filename.\n tar -xzf rarlinux-x64-*.tar.gz\n\n # Move the binary to a standard location in your system's PATH\n sudo mv rar/rar /usr/local/bin/\n\n # Ensure it has execute permissions\n sudo chmod +x /usr/local/bin/rar\n ```\n\n### Windows\n\n1. Download the \"WinRAR and RAR command line tools\" from [rarlab.com/download.htm](https://www.rarlab.com/download.htm).\n2. Extract the downloaded archive.\n3. Copy the `rar.exe` file to a folder that is included in your system's `PATH` environment variable. A common and reliable location is `C:\\Windows\\System32`.\n4. If Stirling PDF is already running, restart it to ensure it recognizes the updated `PATH`.\n\n### macOS\n\nThe recommended method is to use the [Homebrew](https://brew.sh/) package manager.\n\n```bash\nbrew install rar\n```\n\n-----\n\n## Step 2: Configure Stirling PDF\n\nAfter installing `rar` on your host system, follow the appropriate instructions for your environment.\n\n### For Non-Docker Users\n\nIf you installed Stirling PDF directly on your operating system (without Docker), no further configuration is needed. As long as the `rar` command is available in your system's `PATH`, Stirling PDF will automatically (after restart) detect and use it.\n\n### For Docker Users\n\nFor the binary to be accessible inside the container, you have to mount the binary as a volume.\n\nUpdate your `docker-compose.yml` to include the volume mount. The path on the host side must match where you installed `rar`.\n\n```yaml\nservices:\n stirling-pdf:\n image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest\n ports:\n - '8080:8080'\n volumes:\n - ./StirlingPDF/trainingData:/usr/share/tessdata\n - ./StirlingPDF/extraConfigs:/configs\n - ./StirlingPDF/customFiles:/customFiles/\n - ./StirlingPDF/logs:/logs/\n - ./StirlingPDF/pipeline:/pipeline/\n # Add the following line to mount the rar binary\n - /usr/local/bin/rar:/usr/local/bin/rar:ro\n```\n\n**Note for Windows Docker Users:**\nThe host path must use forward slashes. For example, if you placed `rar.exe` in `C:\\Program Files\\RAR`, your volume mount would look like this:\n\n```yaml\n# Example for Windows host path\n- \"C:/Program Files/RAR/rar.exe:/usr/local/bin/rar:ro\"\n```\n\n-----\n\n## Step 3: Verification\n\nConfirm that Stirling PDF can access the `rar` command.\n\n* **For Docker Users:** Execute a command inside the running container.\n\n ```bash\n docker exec -it stirling-pdf rar\n ```\n\n* **For Non-Docker Users:** Check if the `rar` command is recognized in your terminal.\n\n ```bash\n # On Linux and macOS\n which rar\n\n # On Windows\n where rar\n ```\n\nIn both cases, a successful setup will display the RAR version and usage information. An error like \"command not found\" means there is a problem with the installation or `PATH`.\n\n-----\n\n## Important Considerations\n\n### License Note\n\nRAR is shareware. While it is free to use for personal, non-commercial purposes, business or commercial use may require purchasing a license. Please review the official RAR license terms on the RARLAB website for complete details.\n\n### Alternative: Use the CBZ Format\n\nFor broader compatibility and to avoid proprietary software, using the **CBZ (Comic Book ZIP)** format is highly recommended.\n\n* CBZ uses the open and universal ZIP standard.\n* The **PDF to CBZ** tool is enabled in Stirling PDF by default and requires no extra software.\n* CBZ is supported by virtually all modern comic book reader applications.", + "sourcePath": "docs/Configuration/Customisation/PDF to CBR Conversion.md", + "editUrl": "https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/Configuration/Customisation/PDF to CBR Conversion.md" + }, + "configuration/customisation/ui-customisation": { + "id": "configuration/customisation/ui-customisation", + "title": "UI Customisation", + "section": "configuration/customisation", + "markdown": "Stirling PDF allows straightforward customization of the application name and appearance to make Stirling PDF your own.\n\n## Application Name Settings\nThis setting controls the application name:\n- `appNameNavbar` - Used as the browser tab title and as the issuer name shown in authenticator apps for two-factor (TOTP) login. Despite its name it is not shown in the navigation bar (which displays the logo), so do not leave it blank if you use TOTP. Empty falls back to \"Stirling PDF\".\n\n## Show update notifications\nThese settings (in Settings.yml) control system behavior and customization capabilities:\n- `showUpdate` - Controls whether update notifications are displayed\n- `showUpdateOnlyAdmin` - When true, restricts update notifications to admin users only (requires `showUpdate: true`)\n\n## UI Customization Options\n\n### In-App Settings Management (Recommended)\n\nIf you have login enabled and are logged in as an admin, you can configure all settings directly in the application through the **Settings** menu. No need to edit `settings.yml` manually!\n\n**How to access:**\n1. Enable login: `SECURITY_ENABLELOGIN=true`\n2. Log in as an admin user\n3. Navigate to **Settings** in the application\n4. Configure all options through the UI\n5. Changes apply immediately\n\n**Available customizations:**\n- Application name and branding\n- Update notification settings\n- Language settings\n- Theme preferences\n- Logo style (classic/modern)\n\nTo replace the bundled logo with your own, see [Static File Overrides](doc:configuration/customisation/other-customisations) - you drop your logo files into `customFiles/static/