diff --git a/Makefile b/Makefile
index ecf101f1d55..2c7d762f83e 100644
--- a/Makefile
+++ b/Makefile
@@ -91,4 +91,5 @@ openapi:
# Serve the mkdocs site w/ live reload
.PHONY: docs
docs:
- mkdocs serve
+ cd docs && pnpm install && \
+ pnpm run dev
diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs
index 46bfcf28d39..8f1d861a5b6 100644
--- a/docs/astro.config.mjs
+++ b/docs/astro.config.mjs
@@ -82,12 +82,7 @@ export default defineConfig({
label: 'Features',
autogenerate: { directory: 'features' },
},
- {
- label: 'Workflows',
- autogenerate: { directory: 'workflows' },
- collapsed: true,
- },
- {
+ {
label: 'Development',
autogenerate: { directory: 'development', collapsed: true },
collapsed: true,
diff --git a/docs/src/content/docs/development/Architecture/invocations.mdx b/docs/src/content/docs/development/Architecture/invocations.mdx
index d582d73ba92..08ebdda6a93 100644
--- a/docs/src/content/docs/development/Architecture/invocations.mdx
+++ b/docs/src/content/docs/development/Architecture/invocations.mdx
@@ -316,7 +316,7 @@ new Invocation ready to be used.
Once you've created a Node, the next step is to share it with the community! The
best way to do this is to submit a Pull Request to add the Node to the
-[Community Nodes](/workflows/community-nodes/) list. If you're not sure how to do that,
+[Community Nodes](/features/workflows/community-nodes) list. If you're not sure how to do that,
take a look a at our [contributing nodes overview](/development/guides/creating-nodes/).
## Advanced
diff --git a/docs/src/content/docs/features/text-tool.mdx b/docs/src/content/docs/features/Canvas/text-tool.mdx
similarity index 98%
rename from docs/src/content/docs/features/text-tool.mdx
rename to docs/src/content/docs/features/Canvas/text-tool.mdx
index a12a63c7e20..6e223767d8e 100644
--- a/docs/src/content/docs/features/text-tool.mdx
+++ b/docs/src/content/docs/features/Canvas/text-tool.mdx
@@ -1,5 +1,7 @@
---
title: Text Tool
+sidebar:
+ order: 3
---
import { LinkCard } from '@astrojs/starlight/components';
diff --git a/docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx b/docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx
new file mode 100644
index 00000000000..0de968d2e29
--- /dev/null
+++ b/docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx
@@ -0,0 +1,624 @@
+---
+title: Multi-User Administrator Guide
+description: How to set up and manage a multi-user InvokeAI installation.
+sidebar:
+ order: 4
+---
+
+import { Steps } from '@astrojs/starlight/components'
+
+## Overview
+
+This guide is for administrators managing a multi-user InvokeAI
+installation. It covers initial setup, user management, security best
+practices, and troubleshooting.
+
+## Prerequisites
+
+Before enabling multi-user support, ensure you have:
+
+- InvokeAI installed and running
+- Access to the server filesystem (for initial setup)
+- Understanding of your deployment environment
+- Backup of your existing data (recommended)
+
+## Initial Setup
+
+### Activating Multiuser Mode
+
+To put InvokeAI into multiuser mode, you will need to add the option `multiuser: true` to its configuration file. This file is located at `INVOKEAI_ROOT/invokeai.yaml`. With the InvokeAI backend halted, add the new configuration option to the end of the file with a text editor so that it looks like this:
+
+```yaml
+# Internal metadata - do not edit:
+schema_version: 4.0.2
+
+# Enable/disable multi-user mode
+multiuser: true
+```
+
+Then restart the InvokeAI server backend from the command line or using the launcher.
+
+:::note[Reverting to single-user mode]
+If at any time you wish to revert to single-user mode, simply comment out the `multiuser` line, or change "true" to "false". Then restart the server. Because of the way that browsers cache pages, users with open InvokeAI sessions may need to force-refresh their browsers.
+:::
+
+### First Administrator Account
+
+When InvokeAI starts for the first time in multi-user mode, you'll see the **Administrator Setup** dialog.
+
+**Setup Steps:**
+
+
+1. **Email Address**: Enter a valid email address (this becomes your username)
+
+ - Example: `admin@example.com` or `admin@localhost` for testing
+ - Must be a valid email format
+ - Cannot be changed later without database access
+
+2. **Display Name**: Enter a friendly name
+
+ - Example: "System Administrator" or your real name
+ - Can be changed later in your profile
+ - Visible to other users in shared contexts
+
+3. **Password**: Create a strong administrator password
+
+ - **Minimum requirements:**
+
+ - At least 8 characters long
+ - Contains uppercase letters (A-Z)
+ - Contains lowercase letters (a-z)
+ - Contains numbers (0-9)
+
+ - **Recommended:**
+
+ - Use 12+ characters
+ - Include special characters (!@#$%^&*)
+ - Use a password manager to generate and store
+ - Don't reuse passwords from other services
+
+4. **Confirm Password**: Re-enter the password
+
+5. Click **Create Administrator Account**
+
+
+:::caution[Important]
+Store these credentials securely! The first administrator account can reset the password to something new, but cannot retrieve a lost one.
+:::
+
+### Configuration
+
+InvokeAI can run in single-user or multi-user mode, controlled by the `multiuser` configuration option in `invokeai.yaml`:
+
+```yaml
+# Enable/disable multi-user mode
+multiuser: true # Enable multi-user mode (requires authentication)
+# multiuser: false # Single-user mode (no authentication required)
+# If the multiuser option is absent, single-user mode is used
+
+# Database configuration
+use_memory_db: false # Use persistent database
+db_path: databases/invokeai.db # Database location
+
+# Session configuration (multi-user mode only)
+jwt_secret_key: "your-secret-key-here" # Auto-generated if not specified
+jwt_token_expiry_hours: 24 # Default session timeout
+jwt_remember_me_days: 7 # "Remember me" duration
+```
+
+:::caution[Mode Switching Behavior]
+**Switching to Single-User Mode:** If boards or images were created in multi-user mode, they will all be combined into a single unified view when switching to single-user mode.
+
+**Switching to Multi-User Mode:** Legacy boards and images created under single-user mode will be owned by an internal user named "system." Only the Administrator will have access to these legacy assets. A utility to migrate these legacy assets to another user will be part of a future release.
+:::
+
+### Migration from Single-User
+
+When upgrading from a single-user installation or switching modes:
+
+
+1. **Automatic Migration**: The database will automatically migrate to multi-user schema when multi-user mode is first enabled
+2. **Legacy Data Ownership**: Existing data (boards, images, workflows) created in single-user mode is assigned to an internal user named "system"
+3. **Administrator Access**: Only administrators will have access to legacy "system"-owned assets when in multi-user mode
+4. **No Data Loss**: All existing content is preserved
+
+
+**Migration Process:**
+
+```bash
+# Backup your database first
+cp databases/invokeai.db databases/invokeai.db.backup
+
+# Enable multi-user mode in invokeai.yaml
+# multiuser: true
+
+# Start InvokeAI (migration happens automatically)
+invokeai-web
+
+# Complete the administrator setup dialog
+# Legacy data will be owned by "system" user
+```
+
+:::note[Legacy Asset Migration]
+A utility to migrate legacy "system"-owned assets to specific user accounts will be available in a future release. Until then, administrators can access and manage all legacy content.
+:::
+
+## User Management
+
+### Creating Users
+
+Administrators can create and modify users (including other
+administrators) via a built-in web interface or using command-line
+scripts.
+
+#### **Via the Web Frontend:**
+
+Please see the Multi-User Guide's section on [Adding and Modifying Users](../user-guide#adding-and-modifying-users)
+for a walk-through.
+
+#### **Via Command Line Scripts:**
+
+##### Command-line User Management Scripts
+
+Administrators can also use a series of command-line scripts to add, modify, or delete users. If you use the launcher, click the ">" icon to enter the command-line interface. Otherwise, if you are a native command-line user, activate the InvokeAI environment from your terminal.
+
+All command-line arguments are optional. The scripts will prompt you to provide any missing arguments.
+
+The commands are:
+
+| Name | Function | Example CLI Usage |
+|--------------------|---------------|--------------------|
+|**invoke-useradd** | add a user | `invoke-useradd --email user@example.com --name "Example User" --password "badpassword"` |
+|**invoke-usermod** | modify a user | `invoke-usermod --email user@example.com --name "Mr. Example User" --password "8adsf2**%"` |
+|**invoke-userdel** | delete a user | `invoke-userdel --email user@example.com --force` |
+|**invoke-userlist** | list all users| `invoke-userlist` |
+
+Pass the `--help` argument to get the usage of each script. For example:
+
+```bash
+> invoke-useradd --help
+usage: invoke-useradd [-h] [--root ROOT] [--email EMAIL] [--password PASSWORD] [--name NAME] [--admin]
+
+Add a user to the InvokeAI database
+
+options:
+ -h, --help show this help message and exit
+ --root ROOT, -r ROOT Path to the InvokeAI root directory. If omitted, the root is resolved in this order: the $INVOKEAI_ROOT environment
+ variable, the active virtual environment's parent directory, or $HOME/invokeai.
+ --email EMAIL, -e EMAIL
+ User email address
+ --password PASSWORD, -p PASSWORD
+ User password
+ --name NAME, -n NAME User display name (optional)
+ --admin, -a Make user an administrator
+
+If no arguments are provided, the script will run in interactive mode.
+```
+
+:::danger[Data Loss]
+Be aware that deleting a user permanently removes all their
+content and settings, and will remove their access to the boards and images they have created.
+However, their physical image files will continue to reside in `outputs/images` until a gallery maintenance script is
+run to remove orphan images.
+Back up the database first if recovery might be needed.
+:::
+
+### Viewing User Activity
+
+**Queue Management:**
+
+
+1. Navigate to **Admin** → **Queue Overview**
+2. View all users' active and pending generations
+3. Filter by user
+4. Cancel stuck or problematic tasks
+
+
+**User Statistics:**
+
+- Number of boards created
+- Number of images generated
+- Storage usage (if enabled)
+- Last login time
+
+## Model Management
+
+As an administrator, you have full access to the [Model
+Manager](/concepts/models) and can install, edit and delete
+models just as in single-user mode. Unprivileged users, however, can
+view the models previously installed, but cannot add or modify them.
+
+## Security
+
+### Password Policies
+
+**Enforced Requirements:**
+
+- Minimum 8 characters
+- Must contain uppercase letters
+- Must contain lowercase letters
+- Must contain numbers
+
+**Recommended Policies:**
+
+- Require 12+ character passwords
+- Include special characters
+- Implement password rotation every 90 days
+- Prevent password reuse
+- Use multi-factor authentication (when available)
+
+### Session Management
+
+**Session Security and Token Management:**
+
+This system uses stateless JWT tokens with HMAC signatures to identify users after they provide their initial credentials. The tokens will persist for 24 hours by default, or for 7 days if the user clicks the "Remember me" checkbox at login. Expired tokens are automatically rejected and the user will have to log in again.
+
+At the client side, tokens are stored in browser localStorage. Logging out clears them. No server-side session storage is required.
+
+The tokens include the user's ID, email, and admin status, along with an HMAC signature.
+
+### Secret Key Management
+
+**Important:** The JWT secret key must be kept confidential.
+
+To generate tokens, each InvokeAI instance has a distinct secret JWT key that must be kept confidential. The key is stored in the `app_settings` table of the InvokeAI database with in a field value named `jwt_secret`.
+
+The secret key is automatically generated during database creation or migration. If you wish to change the key, you may generate a replacement using either of these commands:
+
+```bash
+# Python
+python -c "import secrets; print(secrets.token_urlsafe(32))"
+
+# OpenSSL
+openssl rand -base64 32
+```
+
+Then cut and paste the printed secret into this Sqlite3 command:
+
+```bash
+sqlite3 INVOKE_ROOT/databases/invokeai.db 'update app_settings set value="THE_SECRET" where key="jwt_secret"'
+```
+
+(replace INVOKE_ROOT with your InvokeAI root directory and THE_SECRET with the new secret).
+
+After this, restart the server. All logged in users will be logged out and will need to provide their usernames and passwords again.
+
+### Hosting a Shared InvokeAI Instance
+
+The multiuser feature allows you to run an InvokeAI backend that can be accessed by your friends and family across your home network. It is also possible to host a backend that is accessible over the Internet.
+
+By default, InvokeAI runs on `localhost`, IP address `127.0.0.1`, which is only accessible to browsers running on the same machine as the backend. To make the backend accessible to any machine on your home or work LAN, add the line `host: 0.0.0.0` to the InvokeAI configuration file, usually stored at `INVOKE_ROOT/invokeai.yaml`.
+
+Here is a minimal example.
+
+```yaml
+# Internal metadata - do not edit:
+schema_version: 4.0.2
+
+# Put user settings here - see https://invoke-ai.github.io/InvokeAI/configuration/:
+multiuser: true
+host: 0.0.0.0
+```
+
+After relaunching the backend you will be able to reach the server from other machines on the LAN using the server machine's IP address or hostname and port 9090.
+
+#### Making InvokeAI Accessible to the Internet
+
+:::danger[Use at your own risk]
+The InvokeAI team has done its best to make the software free of exploitable bugs, but the software has not undergone a rigorous security audit or intrusion testing. Use at your own risk.
+:::
+
+It is also possible to create a (semi) public server accessible from the Internet. The details of how to do this depend very much on your home or corporate router/firewall system and are beyond the scope of this document.
+
+If you expose InvokeAI to the Internet, there are a number of precautions to take. Here is a brief list of recommended network security practices.
+
+**HTTPS Configuration:**
+
+For internet deployments, always use HTTPS:
+
+```nginx
+# Use a reverse proxy like nginx or Traefik
+# Example nginx configuration:
+
+server {
+ listen 443 ssl http2;
+ server_name invoke.example.com;
+
+ ssl_certificate /path/to/cert.pem;
+ ssl_certificate_key /path/to/key.pem;
+
+ location / {
+ proxy_pass http://localhost:9090;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # WebSocket support
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+}
+```
+
+**Firewall Rules:**
+
+It is best to restrict access to trusted networks and remote IP addresses, or use a VPN to connect to your home network. Rate limit connections to InvokeAI's authentication endpoint `http://your.host:9090/login`.
+
+**Backup and Recovery:**
+
+It is always a good idea to periodically backup your InvokeAI database and images, but especially
+so if the server is publicly accessible to the Internet.
+
+**Manual Backup:**
+
+```bash
+# Stop InvokeAI
+# Copy database file
+cd INVOKE_ROOT
+cp databases/invokeai.db databases/invokeai.db.$(date +%Y%m%d)
+
+# Or create compressed backup
+tar -czf invokeai_backup_$(date +%Y%m%d).tar.gz databases/
+```
+
+**Automated Backup Script:**
+
+```bash
+#!/bin/bash
+# backup_invokeai.sh
+
+INVOKE_ROOT="/path/to/invoke_root"
+BACKUP_DIR="/path/to/backups"
+DB_PATH="$INVOKE_ROOT/databases/invokeai.db"
+DATE=$(date +%Y%m%d_%H%M%S)
+
+# Create backup directory
+mkdir -p "$BACKUP_DIR"
+
+# Copy database
+cp "$DB_PATH" "$BACKUP_DIR/invokeai_$DATE.db"
+
+# Keep only last 30 days
+find "$BACKUP_DIR" -name "invokeai_*.db" -mtime +30 -delete
+
+echo "Backup completed: invokeai_$DATE.db"
+```
+
+**Schedule with cron:**
+
+```bash
+# Edit crontab
+crontab -e
+
+# Add daily backup at 2 AM
+0 2 * * * /path/to/backup_invokeai.sh
+```
+
+**Restore from Backup:**
+
+```bash
+# Stop InvokeAI
+# Replace current database with backup
+cd INVOKE_ROOT
+cp databases/invokeai.db databases/invokeai.db.old # Save current
+cp databases/invokeai_backup.db databases/invokeai.db
+
+# Restart InvokeAI
+invokeai-web
+```
+
+**Disaster Recovery — Complete System Backup:**
+
+Include these directories/files:
+
+- `databases/` — All database files
+- `models/` — Installed models (if locally stored)
+- `outputs/` — Generated images
+- `invokeai.yaml` — Configuration file
+- Any custom scripts or modifications
+
+**Recovery Process:**
+
+
+1. Install InvokeAI on new system
+2. Restore configuration file
+3. Restore database directory
+4. Restore models and outputs
+5. Verify file permissions
+6. Start InvokeAI and test
+
+
+## Troubleshooting
+
+### User Cannot Login
+
+**Symptom:** User reports unable to log in
+
+**Diagnosis:**
+
+1. Verify account exists and is active
+
+ ```bash
+ sqlite3 databases/invokeai.db "SELECT * FROM users WHERE email = 'user@example.com';"
+ ```
+
+2. Check password (have user try resetting)
+3. Verify account is active (`is_active = 1`)
+4. Check for account lockout (if implemented)
+
+**Solutions:**
+
+- Reset user password
+- Reactivate disabled account
+- Verify email address is correct
+- Check system logs for auth errors
+
+### Database Locked Errors
+
+**Symptom:** "Database is locked" errors
+
+**Causes:**
+
+- Concurrent write operations
+- Long-running transactions
+- Backup process accessing database
+- File system issues
+
+**Solutions:**
+
+```bash
+# Check for locks
+fuser databases/invokeai.db
+
+# Increase timeout (in config)
+# Or switch to WAL mode:
+sqlite3 databases/invokeai.db "PRAGMA journal_mode=WAL;"
+```
+
+### Forgotten Admin Password
+
+**Recovery Process:**
+
+
+1. Stop InvokeAI
+2. Direct database access:
+
+ ```bash
+ sqlite3 databases/invokeai.db
+ ```
+
+3. Reset admin password (requires password hash):
+
+ ```sql
+ -- Generate hash first using Python:
+ -- from passlib.context import CryptContext
+ -- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+ -- print(pwd_context.hash("NewPassword123"))
+
+ UPDATE users
+ SET password_hash = '$2b$12$...'
+ WHERE email = 'admin@example.com';
+ ```
+
+
+4. Restart InvokeAI
+
+**Alternative:** Remove `jwt_secret_key` from config to trigger setup wizard (will create new admin).
+
+### Performance Issues
+
+**Symptom:** Slow generation or UI
+
+**Diagnosis:**
+
+
+1. Check active generation count
+2. Review resource usage (CPU/GPU/RAM)
+3. Check database size and performance
+4. Review network latency
+
+
+**Solutions:**
+
+- Limit concurrent generations
+- Increase hardware resources
+- Optimize database (`VACUUM`, `ANALYZE`)
+- Add indexes for slow queries
+- Consider load balancing
+
+### Migration Failures
+
+**Symptom:** Database migration fails on upgrade
+
+**Prevention:**
+
+- Always backup before upgrading
+- Test migration on copy of database
+- Review migration logs
+
+**Recovery:**
+
+```bash
+# Restore backup
+cp databases/invokeai.db.backup databases/invokeai.db
+
+# Try migration again with verbose logging
+invokeai-web --log-level DEBUG
+```
+
+## Configuration Reference
+
+### Complete Configuration Example for a Public Site
+
+```yaml
+# invokeai.yaml - Multi-user configuration
+
+# Internal metadata - do not edit:
+schema_version: 4.0.2
+
+# Put user settings here
+multiuser: true
+
+# Server
+host: "0.0.0.0"
+port: 9090
+
+# Performance
+enable_partial_loading: true
+precision: float16
+pytorch_cuda_alloc_conf: "backend:cudaMallocAsync"
+hashing_algorithm: blake3_multi
+```
+
+## Frequently Asked Questions
+
+### How many users can InvokeAI support?
+
+The backend will support dozens of concurrent users. However, because the image generation queue is single-threaded, image generation tasks are processed on a first-come, first-serve basis. This means that a user may have to wait for all the other users' image generation jobs to complete before their generation job starts to execute.
+
+A future version of InvokeAI may support concurrent execution on systems with multiple GPUs/graphics cards.
+
+### Can I integrate with existing authentication systems?
+
+OAuth2/OpenID Connect support is planned for a future release. Currently, InvokeAI uses its own authentication system.
+
+### How do I audit user actions?
+
+Full audit logging is planned for a future release. Currently, you can:
+
+- Monitor the generation queue
+- Review database changes
+- Check application logs
+
+### Can users have different model access?
+
+Currently all users can view and use all installed models. Per-user
+model access is a possible enhancement. Please let the development
+team know if you want this feature.
+
+### How do I handle user data when they leave?
+
+Best practice:
+
+
+1. Deactivate the account first
+2. Transfer ownership of shared boards
+3. After transition period, delete the account
+4. Or keep the account deactivated for audit purposes
+
+
+### What's the licensing impact of multi-user mode?
+
+InvokeAI remains under its existing license. Multi-user mode does not change licensing terms.
+
+## Getting Help
+
+### Support
+
+- **General Documentation**: [InvokeAI Docs](https://invoke.ai/)
+- **User Guide**: [For Users](/features/multi-user-mode/user-guide/)
+- **API Guide**: [For Developers](/features/multi-user-mode/api-guide/)
+- **Discord**: [Join Community](https://discord.gg/ZmtBAhwWhy)
+- **GitHub Issues**: [Report Problems](https://github.com/invoke-ai/InvokeAI/issues)
diff --git a/docs/src/content/docs/features/Multi-User Mode/api-guide.mdx b/docs/src/content/docs/features/Multi-User Mode/api-guide.mdx
new file mode 100644
index 00000000000..459a0f504bf
--- /dev/null
+++ b/docs/src/content/docs/features/Multi-User Mode/api-guide.mdx
@@ -0,0 +1,1233 @@
+---
+title: Multi-User API Guide
+description: How to authenticate and interact with the InvokeAI API in multi-user mode.
+sidebar:
+ order: 5
+---
+import { Steps } from '@astrojs/starlight/components'
+
+## Overview
+
+This guide explains how to interact with InvokeAI's API in both single-user and multi-user modes. The API behavior depends on the `multiuser` configuration setting.
+
+### Single-User vs Multi-User Mode
+
+**Single-User Mode** (`multiuser: false` or option absent):
+
+- No authentication required
+- All API endpoints accessible without tokens
+- Direct API access like previous InvokeAI versions
+- All content visible in unified view
+
+**Multi-User Mode** (`multiuser: true`):
+
+- JWT token authentication required
+- User-scoped access to resources
+- Role-based authorization (admin vs regular user)
+- Data isolation between users
+
+## Authentication (Multi-User Mode Only)
+
+### Authentication Flow
+
+When multi-user mode is enabled, all API endpoints (except `/api/v1/auth/setup` and `/api/v1/auth/login`) require authentication using JWT (JSON Web Token) bearer tokens.
+
+**Authentication Process:**
+
+
+1. **Obtain Token**: POST credentials to `/api/v1/auth/login`
+2. **Store Token**: Save the JWT token securely
+3. **Use Token**: Include token in `Authorization` header for all requests
+4. **Refresh**: Re-authenticate when token expires
+
+
+:::note[Single-User Mode]
+When running in single-user mode (`multiuser: false`), authentication endpoints are not available and authentication headers are not required.
+:::
+
+### Login Endpoint
+
+**Endpoint:** `POST /api/v1/auth/login`
+
+**Request:**
+
+```json
+{
+ "email": "user@example.com",
+ "password": "SecurePassword123",
+ "remember_me": false
+}
+```
+
+**Response (Success):**
+
+```json
+{
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
+ "user": {
+ "user_id": "abc123",
+ "email": "user@example.com",
+ "display_name": "John Doe",
+ "is_admin": false,
+ "is_active": true,
+ "created_at": "2024-01-15T10:00:00Z"
+ },
+ "expires_in": 86400
+}
+```
+
+**Response (Error):**
+
+```json
+{
+ "detail": "Incorrect email or password"
+}
+```
+
+**Status Codes:**
+
+- `200 OK` — Authentication successful
+- `401 Unauthorized` — Invalid credentials
+- `403 Forbidden` — Account disabled
+- `422 Unprocessable Entity` — Invalid request format
+
+### Using the Token
+
+Include the JWT token in the `Authorization` header with the `Bearer` scheme:
+
+**HTTP Header:**
+
+```
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+```
+
+**Example HTTP Request:**
+
+```http
+GET /api/v1/boards HTTP/1.1
+Host: localhost:9090
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+Content-Type: application/json
+```
+
+### Token Expiration
+
+Tokens have a limited lifetime:
+
+- **Default**: 24 hours (86400 seconds)
+- **Remember Me**: 7 days (604800 seconds)
+
+**Handling Expiration:**
+
+```python
+import requests
+import time
+
+def api_request(url, token, max_retries=1):
+ headers = {"Authorization": f"Bearer {token}"}
+ response = requests.get(url, headers=headers)
+
+ if response.status_code == 401: # Token expired
+ # Re-authenticate and retry
+ new_token = login()
+ headers = {"Authorization": f"Bearer {new_token}"}
+ response = requests.get(url, headers=headers)
+
+ return response
+```
+
+### Logout Endpoint
+
+**Endpoint:** `POST /api/v1/auth/logout`
+
+**Request:**
+
+```http
+POST /api/v1/auth/logout HTTP/1.1
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+```
+
+**Response:**
+
+```json
+{
+ "success": true
+}
+```
+
+**Note:** With JWT tokens, logout is primarily client-side (delete token). Server-side session invalidation may be added in future releases.
+
+## Code Examples
+
+### Python
+
+**Using `requests` library:**
+
+```python
+import requests
+import json
+
+class InvokeAIClient:
+ def __init__(self, base_url="http://localhost:9090"):
+ self.base_url = base_url
+ self.token = None
+
+ def login(self, email, password, remember_me=False):
+ """Authenticate and store token."""
+ url = f"{self.base_url}/api/v1/auth/login"
+ payload = {
+ "email": email,
+ "password": password,
+ "remember_me": remember_me
+ }
+
+ response = requests.post(url, json=payload)
+ response.raise_for_status()
+
+ data = response.json()
+ self.token = data["token"]
+ return data["user"]
+
+ def _get_headers(self):
+ """Get headers with authentication token."""
+ if not self.token:
+ raise Exception("Not authenticated. Call login() first.")
+
+ return {
+ "Authorization": f"Bearer {self.token}",
+ "Content-Type": "application/json"
+ }
+
+ def get_boards(self):
+ """Get user's boards."""
+ url = f"{self.base_url}/api/v1/boards/"
+ response = requests.get(url, headers=self._get_headers())
+ response.raise_for_status()
+ return response.json()
+
+ def create_board(self, board_name):
+ """Create a new board."""
+ url = f"{self.base_url}/api/v1/boards/"
+ payload = {"board_name": board_name}
+
+ response = requests.post(
+ url,
+ json=payload,
+ headers=self._get_headers()
+ )
+ response.raise_for_status()
+ return response.json()
+
+ def logout(self):
+ """Logout and clear token."""
+ url = f"{self.base_url}/api/v1/auth/logout"
+ response = requests.post(url, headers=self._get_headers())
+ self.token = None
+ return response.json()
+
+# Usage
+client = InvokeAIClient()
+user = client.login("user@example.com", "SecurePassword123")
+print(f"Logged in as: {user['display_name']}")
+
+boards = client.get_boards()
+print(f"User has {len(boards['items'])} boards")
+
+new_board = client.create_board("My New Board")
+print(f"Created board: {new_board['board_name']}")
+
+client.logout()
+```
+
+**Error Handling:**
+
+```python
+import requests
+from requests.exceptions import HTTPError
+
+def safe_api_call(client, method, *args, **kwargs):
+ """Make API call with error handling."""
+ try:
+ func = getattr(client, method)
+ return func(*args, **kwargs)
+
+ except HTTPError as e:
+ if e.response.status_code == 401:
+ print("Authentication failed or token expired")
+ # Re-authenticate
+ client.login(email, password)
+ # Retry
+ return func(*args, **kwargs)
+ elif e.response.status_code == 403:
+ print("Permission denied")
+ elif e.response.status_code == 404:
+ print("Resource not found")
+ else:
+ print(f"API error: {e.response.status_code}")
+ print(e.response.text)
+
+ raise
+
+# Usage
+try:
+ boards = safe_api_call(client, "get_boards")
+except Exception as e:
+ print(f"Failed to get boards: {e}")
+```
+
+### JavaScript/TypeScript
+
+**Using `fetch` API:**
+
+```javascript
+class InvokeAIClient {
+ constructor(baseUrl = 'http://localhost:9090') {
+ this.baseUrl = baseUrl;
+ this.token = null;
+ }
+
+ async login(email, password, rememberMe = false) {
+ const response = await fetch(`${this.baseUrl}/api/v1/auth/login`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ email,
+ password,
+ remember_me: rememberMe,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Login failed: ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ this.token = data.token;
+
+ // Store token in localStorage
+ localStorage.setItem('invokeai_token', data.token);
+
+ return data.user;
+ }
+
+ getHeaders() {
+ if (!this.token) {
+ throw new Error('Not authenticated. Call login() first.');
+ }
+
+ return {
+ 'Authorization': `Bearer ${this.token}`,
+ 'Content-Type': 'application/json',
+ };
+ }
+
+ async getBoards() {
+ const response = await fetch(`${this.baseUrl}/api/v1/boards/`, {
+ headers: this.getHeaders(),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to get boards: ${response.statusText}`);
+ }
+
+ return response.json();
+ }
+
+ async createBoard(boardName) {
+ const response = await fetch(`${this.baseUrl}/api/v1/boards/`, {
+ method: 'POST',
+ headers: this.getHeaders(),
+ body: JSON.stringify({ board_name: boardName }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to create board: ${response.statusText}`);
+ }
+
+ return response.json();
+ }
+
+ async logout() {
+ const response = await fetch(`${this.baseUrl}/api/v1/auth/logout`, {
+ method: 'POST',
+ headers: this.getHeaders(),
+ });
+
+ this.token = null;
+ localStorage.removeItem('invokeai_token');
+
+ return response.json();
+ }
+}
+
+// Usage
+(async () => {
+ const client = new InvokeAIClient();
+
+ try {
+ const user = await client.login('user@example.com', 'SecurePassword123');
+ console.log(`Logged in as: ${user.display_name}`);
+
+ const boards = await client.getBoards();
+ console.log(`User has ${boards.items.length} boards`);
+
+ const newBoard = await client.createBoard('My New Board');
+ console.log(`Created board: ${newBoard.board_name}`);
+
+ await client.logout();
+ } catch (error) {
+ console.error('Error:', error.message);
+ }
+})();
+```
+
+**TypeScript with Types:**
+
+```typescript
+interface LoginRequest {
+ email: string;
+ password: string;
+ remember_me?: boolean;
+}
+
+interface User {
+ user_id: string;
+ email: string;
+ display_name: string;
+ is_admin: boolean;
+ is_active: boolean;
+ created_at: string;
+}
+
+interface LoginResponse {
+ token: string;
+ user: User;
+ expires_in: number;
+}
+
+interface Board {
+ board_id: string;
+ board_name: string;
+ created_at: string;
+ updated_at: string;
+ deleted_at?: string;
+ cover_image_name?: string;
+}
+
+class InvokeAIClient {
+ private baseUrl: string;
+ private token: string | null = null;
+
+ constructor(baseUrl: string = 'http://localhost:9090') {
+ this.baseUrl = baseUrl;
+ }
+
+ async login(
+ email: string,
+ password: string,
+ rememberMe: boolean = false
+ ): Promise {
+ const response = await fetch(`${this.baseUrl}/api/v1/auth/login`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ email, password, remember_me: rememberMe }),
+ });
+
+ if (!response.ok) {
+ const error = await response.json();
+ throw new Error(error.detail || 'Login failed');
+ }
+
+ const data: LoginResponse = await response.json();
+ this.token = data.token;
+ return data.user;
+ }
+
+ private getHeaders(): HeadersInit {
+ if (!this.token) {
+ throw new Error('Not authenticated');
+ }
+ return {
+ 'Authorization': `Bearer ${this.token}`,
+ 'Content-Type': 'application/json',
+ };
+ }
+
+ async getBoards(): Promise<{ items: Board[] }> {
+ const response = await fetch(`${this.baseUrl}/api/v1/boards/`, {
+ headers: this.getHeaders(),
+ });
+
+ if (!response.ok) {
+ throw new Error('Failed to get boards');
+ }
+
+ return response.json();
+ }
+}
+```
+
+### cURL
+
+**Login:**
+
+```bash
+# Login and extract token
+TOKEN=$(curl -X POST http://localhost:9090/api/v1/auth/login \
+ -H "Content-Type: application/json" \
+ -d '{
+ "email": "user@example.com",
+ "password": "SecurePassword123",
+ "remember_me": false
+ }' | jq -r '.token')
+
+echo "Token: $TOKEN"
+```
+
+**Get Boards:**
+
+```bash
+curl -X GET http://localhost:9090/api/v1/boards/ \
+ -H "Authorization: Bearer $TOKEN" \
+ -H "Content-Type: application/json"
+```
+
+**Create Board:**
+
+```bash
+curl -X POST http://localhost:9090/api/v1/boards/ \
+ -H "Authorization: Bearer $TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "board_name": "My API Board"
+ }'
+```
+
+**Generate Image:**
+
+```bash
+curl -X POST http://localhost:9090/api/v1/sessions/ \
+ -H "Authorization: Bearer $TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "prompt": "A beautiful landscape",
+ "width": 512,
+ "height": 512,
+ "steps": 30
+ }'
+```
+
+## API Endpoint Changes
+
+### Authentication Required
+
+All endpoints now require authentication except:
+
+- `POST /api/v1/auth/setup` — Initial admin setup
+- `POST /api/v1/auth/login` — User login
+
+### User-Scoped Resources
+
+Resources are now filtered by the authenticated user:
+
+**Boards:**
+
+```python
+# Before (single-user)
+GET /api/v1/boards/ # Returns all boards
+
+# After (multi-user)
+GET /api/v1/boards/ # Returns only current user's boards
+```
+
+**Images:**
+
+```python
+# Images are filtered by board ownership
+GET /api/v1/images/ # Only shows images on user's boards
+```
+
+**Workflows:**
+
+```python
+# Returns user's workflows + public workflows
+GET /api/v1/workflows/
+```
+
+**Queue:**
+
+```python
+# Regular users see only their queue items
+GET /api/v1/queue/ # User's queue items
+
+# Administrators see all queue items
+GET /api/v1/queue/ # All users' queue items
+```
+
+### Administrator Endpoints
+
+Some endpoints require administrator privileges:
+
+**User Management:**
+
+```python
+GET /api/v1/users # List users (admin only)
+POST /api/v1/users # Create user (admin only)
+GET /api/v1/users/{id} # Get user (admin only)
+PATCH /api/v1/users/{id} # Update user (admin only)
+DELETE /api/v1/users/{id} # Delete user (admin only)
+```
+
+**Model Management (Write Operations):**
+
+```python
+POST /api/v1/models/install # Install model (admin only)
+DELETE /api/v1/models/i/{key} # Delete model (admin only)
+PATCH /api/v1/models/i/{key} # Update model (admin only)
+PUT /api/v1/models/convert/{key} # Convert model (admin only)
+```
+
+**Model Management (Read Operations):**
+
+```python
+GET /api/v1/models/ # List models (all users)
+GET /api/v1/models/i/{key} # Get model details (all users)
+```
+
+### Error Responses
+
+**401 Unauthorized:**
+
+```json
+{
+ "detail": "Invalid authentication credentials"
+}
+```
+
+Occurs when:
+
+- Token is missing
+- Token is invalid
+- Token is expired
+- Token signature is invalid
+
+**403 Forbidden:**
+
+```json
+{
+ "detail": "Admin privileges required"
+}
+```
+
+Occurs when:
+
+- User attempts admin-only operation
+- Account is disabled
+- Insufficient permissions
+
+**404 Not Found:**
+
+```json
+{
+ "detail": "Resource not found"
+}
+```
+
+Occurs when:
+
+- Resource doesn't exist
+- User doesn't have access to resource
+
+## New API Endpoints
+
+### Authentication Endpoints
+
+#### Setup Administrator
+
+**Endpoint:** `POST /api/v1/auth/setup`
+
+**Description:** Create initial administrator account (only works if no admin exists)
+
+**Request:**
+
+```json
+{
+ "email": "admin@example.com",
+ "display_name": "Administrator",
+ "password": "SecureAdminPass123"
+}
+```
+
+**Response:**
+
+```json
+{
+ "success": true,
+ "user": {
+ "user_id": "abc123",
+ "email": "admin@example.com",
+ "display_name": "Administrator",
+ "is_admin": true,
+ "is_active": true
+ }
+}
+```
+
+#### Get Current User
+
+**Endpoint:** `GET /api/v1/auth/me`
+
+**Description:** Get currently authenticated user's information
+
+**Request:**
+
+```http
+GET /api/v1/auth/me
+Authorization: Bearer
+```
+
+**Response:**
+
+```json
+{
+ "user_id": "abc123",
+ "email": "user@example.com",
+ "display_name": "John Doe",
+ "is_admin": false,
+ "is_active": true,
+ "created_at": "2024-01-15T10:00:00Z",
+ "updated_at": "2024-01-15T10:00:00Z",
+ "last_login_at": "2024-01-15T15:30:00Z"
+}
+```
+
+#### Change Password
+
+**Endpoint:** `POST /api/v1/auth/change-password`
+
+**Description:** Change current user's password
+
+**Request:**
+
+```json
+{
+ "current_password": "OldPassword123",
+ "new_password": "NewPassword456"
+}
+```
+
+**Response:**
+
+```json
+{
+ "success": true
+}
+```
+
+### User Management Endpoints (Admin Only)
+
+#### List Users
+
+**Endpoint:** `GET /api/v1/users`
+
+**Request:**
+
+```http
+GET /api/v1/users?page=1&per_page=20
+Authorization: Bearer
+```
+
+**Response:**
+
+```json
+{
+ "items": [
+ {
+ "user_id": "abc123",
+ "email": "user@example.com",
+ "display_name": "John Doe",
+ "is_admin": false,
+ "is_active": true,
+ "created_at": "2024-01-15T10:00:00Z",
+ "last_login_at": "2024-01-15T15:30:00Z"
+ }
+ ],
+ "page": 1,
+ "pages": 1,
+ "per_page": 20,
+ "total": 5
+}
+```
+
+#### Create User
+
+**Endpoint:** `POST /api/v1/users`
+
+**Request:**
+
+```json
+{
+ "email": "newuser@example.com",
+ "display_name": "New User",
+ "password": "TempPassword123",
+ "is_admin": false
+}
+```
+
+**Response:**
+
+```json
+{
+ "user_id": "xyz789",
+ "email": "newuser@example.com",
+ "display_name": "New User",
+ "is_admin": false,
+ "is_active": true,
+ "created_at": "2024-01-15T16:00:00Z"
+}
+```
+
+#### Update User
+
+**Endpoint:** `PATCH /api/v1/users/{user_id}`
+
+**Request:**
+
+```json
+{
+ "display_name": "Updated Name",
+ "is_active": true,
+ "is_admin": false
+}
+```
+
+**Response:**
+
+```json
+{
+ "user_id": "xyz789",
+ "email": "newuser@example.com",
+ "display_name": "Updated Name",
+ "is_admin": false,
+ "is_active": true
+}
+```
+
+#### Delete User
+
+**Endpoint:** `DELETE /api/v1/users/{user_id}`
+
+**Response:**
+
+```json
+{
+ "success": true
+}
+```
+
+#### Reset User Password
+
+**Endpoint:** `POST /api/v1/users/{user_id}/reset-password`
+
+**Request:**
+
+```json
+{
+ "new_password": "NewTempPass123"
+}
+```
+
+**Response:**
+
+```json
+{
+ "success": true
+}
+```
+
+### Board Sharing Endpoints
+
+#### Share Board
+
+**Endpoint:** `POST /api/v1/boards/{board_id}/share`
+
+**Request:**
+
+```json
+{
+ "user_id": "user123",
+ "permission": "write"
+}
+```
+
+**Response:**
+
+```json
+{
+ "success": true,
+ "share": {
+ "board_id": "board456",
+ "user_id": "user123",
+ "permission": "write",
+ "shared_at": "2024-01-15T16:00:00Z"
+ }
+}
+```
+
+#### List Board Shares
+
+**Endpoint:** `GET /api/v1/boards/{board_id}/shares`
+
+**Response:**
+
+```json
+{
+ "items": [
+ {
+ "user_id": "user123",
+ "display_name": "John Doe",
+ "permission": "write",
+ "shared_at": "2024-01-15T16:00:00Z"
+ }
+ ]
+}
+```
+
+#### Remove Board Share
+
+**Endpoint:** `DELETE /api/v1/boards/{board_id}/share/{user_id}`
+
+**Response:**
+
+```json
+{
+ "success": true
+}
+```
+
+## Best Practices
+
+### Token Storage
+
+**Do:**
+
+- Store tokens securely (keychain, secure storage)
+- Use HTTPS to transmit tokens
+- Clear tokens on logout
+- Handle token expiration gracefully
+
+**Don't:**
+
+- Store tokens in URL parameters
+- Log tokens in plain text
+- Share tokens between users
+- Store tokens in version control
+
+### Error Handling
+
+Always handle authentication errors:
+
+```python
+def make_request(client, func, *args, **kwargs):
+ max_retries = 3
+ retry_count = 0
+
+ while retry_count < max_retries:
+ try:
+ return func(*args, **kwargs)
+ except AuthenticationError:
+ if retry_count >= max_retries - 1:
+ raise
+ # Re-authenticate
+ client.login(email, password)
+ retry_count += 1
+ except Exception as e:
+ logger.error(f"Request failed: {e}")
+ raise
+```
+
+### Rate Limiting
+
+Be mindful of API rate limits:
+
+- Implement exponential backoff for retries
+- Cache frequently accessed data
+- Batch requests when possible
+- Don't hammer the login endpoint
+
+### Connection Management
+
+```python
+import requests
+from requests.adapters import HTTPAdapter
+from urllib3.util.retry import Retry
+
+def create_session():
+ """Create session with retry logic."""
+ session = requests.Session()
+
+ retry = Retry(
+ total=3,
+ backoff_factor=0.3,
+ status_forcelist=[500, 502, 503, 504],
+ )
+
+ adapter = HTTPAdapter(max_retries=retry)
+ session.mount('http://', adapter)
+ session.mount('https://', adapter)
+
+ return session
+```
+
+## Migration Guide
+
+### Updating Existing Code
+
+**Before (single-user mode):**
+
+```python
+import requests
+
+def get_boards():
+ response = requests.get("http://localhost:9090/api/v1/boards/")
+ return response.json()
+```
+
+**After (multi-user mode):**
+
+```python
+import requests
+
+class APIClient:
+ def __init__(self):
+ self.token = None
+
+ def login(self, email, password):
+ response = requests.post(
+ "http://localhost:9090/api/v1/auth/login",
+ json={"email": email, "password": password}
+ )
+ self.token = response.json()["token"]
+
+ def get_boards(self):
+ headers = {"Authorization": f"Bearer {self.token}"}
+ response = requests.get(
+ "http://localhost:9090/api/v1/boards/",
+ headers=headers
+ )
+ return response.json()
+
+# Usage
+client = APIClient()
+client.login("user@example.com", "password")
+boards = client.get_boards()
+```
+
+### Backward Compatibility
+
+InvokeAI supports both single-user and multi-user modes via the `multiuser` configuration option.
+
+**Configuration:**
+
+```yaml
+# invokeai.yaml
+
+# Single-user mode (no authentication)
+multiuser: false # or omit the option entirely
+
+# Multi-user mode (authentication required)
+multiuser: true
+```
+
+**Checking Mode Programmatically:**
+
+```python
+def is_multiuser_enabled(base_url):
+ """Check if multi-user mode is enabled (authentication required)."""
+ response = requests.get(f"{base_url}/api/v1/boards/")
+ return response.status_code == 401 # 401 = auth required
+
+# Example usage
+base_url = "http://localhost:9090"
+if is_multiuser_enabled(base_url):
+ print("Multi-user mode: authentication required")
+ # Use authenticated API calls
+else:
+ print("Single-user mode: no authentication needed")
+ # Use direct API calls
+```
+
+**Adaptive Client:**
+
+```python
+class AdaptiveInvokeAIClient:
+ def __init__(self, base_url="http://localhost:9090"):
+ self.base_url = base_url
+ self.token = None
+ self.multiuser_mode = self._check_multiuser_mode()
+
+ def _check_multiuser_mode(self):
+ """Detect if multi-user mode is enabled."""
+ try:
+ response = requests.get(f"{self.base_url}/api/v1/boards/")
+ return response.status_code == 401
+ except:
+ return False
+
+ def login(self, email, password):
+ """Login (only needed in multi-user mode)."""
+ if not self.multiuser_mode:
+ print("Single-user mode: login not required")
+ return
+
+ response = requests.post(
+ f"{self.base_url}/api/v1/auth/login",
+ json={"email": email, "password": password}
+ )
+ self.token = response.json()["token"]
+
+ def _get_headers(self):
+ """Get headers (with auth token if in multi-user mode)."""
+ if self.multiuser_mode and self.token:
+ return {"Authorization": f"Bearer {self.token}"}
+ return {}
+
+ def get_boards(self):
+ """Get boards (works in both modes)."""
+ response = requests.get(
+ f"{self.base_url}/api/v1/boards/",
+ headers=self._get_headers()
+ )
+ return response.json()
+```
+
+## OpenAPI/Swagger Documentation
+
+InvokeAI provides OpenAPI documentation for all endpoints.
+
+**Access Swagger UI:**
+
+```
+http://localhost:9090/docs
+```
+
+**Download OpenAPI Schema:**
+
+```bash
+curl http://localhost:9090/openapi.json > invokeai_openapi.json
+```
+
+**Generate Client Code:**
+
+Use tools like `openapi-generator` to generate client libraries:
+
+```bash
+# Generate Python client
+openapi-generator generate \
+ -i http://localhost:9090/openapi.json \
+ -g python \
+ -o ./invokeai-client
+
+# Generate TypeScript client
+openapi-generator generate \
+ -i http://localhost:9090/openapi.json \
+ -g typescript-fetch \
+ -o ./invokeai-client-ts
+```
+
+## Security Considerations
+
+### HTTPS
+
+Always use HTTPS in production:
+
+```python
+# Development
+client = InvokeAIClient("http://localhost:9090")
+
+# Production
+client = InvokeAIClient("https://invoke.example.com")
+```
+
+### Token Security
+
+Protect JWT tokens:
+
+```python
+# Never log tokens
+logger.info(f"User logged in") # Good
+logger.info(f"Token: {token}") # Bad!
+
+# Use environment variables for credentials
+import os
+email = os.environ.get("INVOKEAI_EMAIL")
+password = os.environ.get("INVOKEAI_PASSWORD")
+```
+
+### Input Validation
+
+Always validate user input:
+
+```python
+import re
+
+def validate_email(email):
+ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
+ return re.match(pattern, email) is not None
+
+def validate_password(password):
+ """Check password meets requirements."""
+ if len(password) < 8:
+ return False, "Password must be at least 8 characters"
+ if not any(c.isupper() for c in password):
+ return False, "Password must contain uppercase letters"
+ if not any(c.islower() for c in password):
+ return False, "Password must contain lowercase letters"
+ if not any(c.isdigit() for c in password):
+ return False, "Password must contain numbers"
+ return True, ""
+```
+
+## Troubleshooting
+
+### Common Issues
+
+**Issue: "Invalid authentication credentials"**
+
+- Token expired — re-authenticate
+- Token malformed — check token string
+- Token signature invalid — check secret key hasn't changed
+
+**Issue: "Admin privileges required"**
+
+- User is not an administrator
+- Use admin account for this operation
+
+**Issue: Token not being sent**
+
+- Check `Authorization` header is present
+- Verify `Bearer` prefix is included
+- Check token isn't truncated
+
+**Issue: CORS errors**
+
+Configure CORS in InvokeAI:
+
+```yaml
+# invokeai.yaml
+cors_origins:
+ - "http://localhost:3000"
+ - "https://myapp.example.com"
+```
+
+## Additional Resources
+
+- [User Guide](./user-guide/) — For end users
+- [Administrator Guide](./admin-guide/) — For administrators
+- [GitHub Repository](https://github.com/invoke-ai/InvokeAI) — Source code
+
+---
+
+**Questions?** Visit the [InvokeAI Discord](https://discord.gg/ZmtBAhwWhy) or check the [FAQ](/troubleshooting/faq/).
diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-1.png b/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-1.png
new file mode 100644
index 00000000000..706039d50cb
Binary files /dev/null and b/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-1.png differ
diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-2.png b/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-2.png
new file mode 100644
index 00000000000..44bf2e180a3
Binary files /dev/null and b/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-2.png differ
diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-3.png b/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-3.png
new file mode 100644
index 00000000000..708f9a85135
Binary files /dev/null and b/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-3.png differ
diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/admin-setup.png b/docs/src/content/docs/features/Multi-User Mode/assets/admin-setup.png
new file mode 100644
index 00000000000..fbe035e5c6e
Binary files /dev/null and b/docs/src/content/docs/features/Multi-User Mode/assets/admin-setup.png differ
diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/user-login-1.png b/docs/src/content/docs/features/Multi-User Mode/assets/user-login-1.png
new file mode 100644
index 00000000000..8c4bec22943
Binary files /dev/null and b/docs/src/content/docs/features/Multi-User Mode/assets/user-login-1.png differ
diff --git a/docs/src/content/docs/features/Multi-User Mode/user-guide.mdx b/docs/src/content/docs/features/Multi-User Mode/user-guide.mdx
new file mode 100644
index 00000000000..6c24e2231d0
--- /dev/null
+++ b/docs/src/content/docs/features/Multi-User Mode/user-guide.mdx
@@ -0,0 +1,363 @@
+---
+title: Multi-User Guide
+description: How to use InvokeAI in multi-user mode as an end user.
+sidebar:
+ order: 3
+---
+import { Steps } from '@astrojs/starlight/components'
+
+## Overview
+
+Multi-User mode is a recent feature (introduced in version 6.12), which allows multiple individuals to share a single InvokeAI server while keeping their work separate and organized. Each user has their own username and login password, images, assets, image boards, customization settings and workflows.
+
+Two types of users are recognized:
+
+- A user with **Administrator** status can add, remove and modify other users, and can install models. They also have the ability to view the full session queue and pause or kill other users' jobs.
+- **Non-administrator** users can modify their own profile but not others. They also do not have the ability to install or configure models, but must ask an Administrator to do this task.
+
+Multiple users can be granted Administrator status.
+
+---
+
+## Getting Started
+
+To activate Multi-User mode, open the `INVOKEAI_ROOT/invokeai.yaml` configuration file in a text editor. Add this line anywhere in the file:
+
+```yaml
+multiuser: true
+```
+
+You may also wish to make InvokeAI available to other machines on your local LAN. Add an additional line to `invokeai.yaml`:
+
+```yaml
+host: 0.0.0.0
+```
+
+Restart the server. It will now be in multi-user mode. If you enabled the `host` option, other users on your home or office LAN will be able to reach it by browsing to the IP address of the machine the backend is running on (`http://host-ip-address:9090`).
+
+:::tip[Do not expose InvokeAI to the internet]
+It is not recommended to expose the InvokeAI host to the internet due to security concerns.
+:::
+
+### Initial Setup (First Time in Multi-User Mode)
+
+If you're the first person to access a fresh InvokeAI installation in multi-user mode, you'll see the **Administrator Setup** dialog:
+
+
+
+Now:
+
+
+1. Enter your email address (this will be your login name)
+2. Create a display name (this will be the name other users see)
+3. Choose a strong password that meets the requirements:
+ - At least 8 characters long
+ - Contains uppercase letters
+ - Contains lowercase letters
+ - Contains numbers
+4. Confirm your password
+5. Click **Create Administrator Account**
+
+
+You'll now be taken to a login screen and can enter the credentials you just created.
+
+### Adding and Modifying Users
+
+If you are logged in as Administrator, you can add additional users. Click on the small "person silhouette" icon at the bottom left of the main Invoke screen and select "User Management:"
+
+
+
+This will take you to the User Management screen...
+
+
+
+...where you can click "Create User" to add a new user.
+
+
+
+The User Management screen also allows you to:
+
+
+1. Temporarily change a user's status to Inactive, preventing them from logging in to Invoke.
+2. Edit a user (by clicking on the pencil icon) to change the user's display name or password.
+3. Permanently delete a user.
+4. Grant a user Administrator privileges.
+
+
+---
+
+## Logging in as a Non-Administrative User
+
+If you are a registered user on the system, enter your email address and password to log in. The Administrator will be able to provide you with the values to use:
+
+
+
+As an unprivileged user you can do pretty much anything that's allowed under single-user mode — generating images, using LoRAs, creating and running workflows, creating image boards — but you are restricted against installing new models, changing low-level server settings, or interfering with other users. More information on user roles is given below.
+
+### Changing your Profile
+
+To change your display name or profile, click on the person silhouette icon at the bottom left of the screen and choose "My Profile". This will take you to a screen that lets you change these values. At this time you can change your display name but not your login ID (ordinarily your contact email address).
+
+---
+
+## Understanding User Roles
+
+In single-user mode, you have access to all features without restrictions. In multi-user mode, InvokeAI has two user roles:
+
+### Regular User
+
+As a regular user, you can:
+
+- Create and manage your own image boards
+- Generate images using all AI tools (Linear, Canvas, Upscale, Workflows)
+- Create, save, and load your own workflows
+- View your own generation queue
+- Customize your UI preferences (theme, hotkeys, etc.)
+- View available models (read-only access to Model Manager)
+- View shared and public boards created by other users
+- View and use workflows marked as shared by other users
+
+You cannot:
+
+- Add, delete, or modify models
+- View or modify other users' private boards, images, or workflows
+- Manage user accounts
+- Access system configuration
+- View or cancel other users' generation tasks
+
+:::tip[The generation queue]
+When two or more users are accessing InvokeAI at the same time, their image generation jobs will be placed on the session queue on a first-come, first-serve basis. This means that you will have to wait for other users' image rendering jobs to complete before yours will start.
+
+When another user's job is running, you will see the image generation progress bar and a queue badge that reads `X/Y`, where "X" is the number of jobs you have queued and "Y" is the total number of jobs queued, including your own and others.
+
+You can also pull up the Queue tab in order to see where your job is in relationship to other queued tasks.
+:::
+
+### Administrator
+
+Administrators have all regular user capabilities, plus:
+
+- Full model management (add, delete, configure models)
+- Create and manage user accounts
+- View and manage all users' generation queues
+- View and manage all users' boards, images, and workflows (including system-owned legacy content)
+- Access system configuration
+- Grant or revoke admin privileges
+
+---
+
+## Working with Your Content in Multi-User Mode
+
+### Image Boards
+
+In multi-user mode, each user can create an unlimited number of boards and organize their images and assets as they see fit. Boards have three visibility levels:
+
+- **Private** (default): Only you (and administrators) can see and modify the board.
+- **Shared**: All users can view the board and its contents, but only you (and administrators) can modify it (rename, archive, delete, or add/remove images).
+- **Public**: All users can view the board. Only you (and administrators) can modify the board's structure (rename, archive, delete).
+
+To change a board's visibility, right-click on the board and select the desired visibility option.
+
+Administrators can see and manage all users' image boards and their contents regardless of visibility settings.
+
+### Going From Multi-User to Single-User Mode
+
+If an InvokeAI instance was in multiuser mode and then restarted in single user mode (by setting `multiuser: false` in the configuration file), all users' boards will be consolidated in one place. Any images that were in "Uncategorized" will be merged together into a single Uncategorized board. If, at a later date, the server is restarted in multi-user mode, the boards and images will be separated and restored to their owners.
+
+### Workflows
+
+Each user has their own private workflow library. Workflows you create are visible only to you by default.
+
+You can share a workflow with other users by marking it as **shared** (public). Shared workflows appear in all users' workflow libraries and can be opened by anyone, but only the owner (or an administrator) can modify or delete them.
+
+To share a workflow, open it and use the sharing controls to toggle its public/shared status.
+
+:::caution[Preexisting workflows after enabling multi-user mode]
+When you enable multi-user mode for the first time on an existing InvokeAI installation, all workflows that were created before multi-user mode was activated will appear in the **shared workflows** section. These preexisting workflows are owned by the internal "system" account and are visible to all users. Administrators can edit or delete these shared legacy workflows. Regular users can view and use them but cannot modify them.
+:::
+
+### The Generation Queue
+
+The queue shows your pending and running generation tasks.
+
+**Queue Features:**
+
+- View your current and completed generations
+- Cancel pending tasks
+- Re-run previous generations
+- Monitor progress in real-time
+
+**Queue Isolation:**
+
+- You will see your own queue items, as well as the items generated by other users, but the generation parameters (e.g. prompts) for other users' jobs are hidden for privacy reasons.
+- Administrators can view all queues for troubleshooting.
+- Your generations won't interfere with other users' tasks.
+
+---
+
+## Customizing Your Experience
+
+### Personal Preferences
+
+Your UI preferences are saved to your account and are restored when you log in:
+
+- **Theme**: Choose between light and dark modes
+- **Hotkeys**: Customize keyboard shortcuts
+- **Canvas Settings**: Default zoom, grid visibility, etc.
+- **Generation Defaults**: Default values for width, height, steps, etc.
+
+These settings are stored per-user and won't affect other users.
+
+---
+
+## Troubleshooting
+
+### Cannot Log In
+
+**Issue:** Login fails with "Incorrect email or password"
+
+**Solutions:**
+
+- Verify you're entering the correct email address
+- Check that Caps Lock is off
+- Try typing the password slowly to avoid mistakes
+- Contact your administrator if you've forgotten your password
+
+**Issue:** Login fails with "Account is disabled"
+
+**Solution:** Contact your administrator to reactivate your account
+
+### Session Expired
+
+**Issue:** You're suddenly logged out and see "Session expired"
+
+**Explanation:** Sessions expire after 24 hours (or 7 days with "remember me")
+
+**Solution:** Simply log in again with your credentials
+
+### Cannot Access Features
+
+**Issue:** Features like Model Manager show "Admin privileges required"
+
+**Explanation:** Some features are restricted to administrators
+
+**Solution:**
+
+- For model viewing: You can view but not modify models
+- For user management: Contact an administrator
+- For system configuration: Contact an administrator
+
+### Missing Boards or Images
+
+**Issue:** Boards or images you created are not visible
+
+**Possible Causes:**
+
+
+1. **Filter Applied:** Check if a filter is hiding content
+2. **Wrong User:** Ensure you're logged in with the correct account
+3. **Archived Board:** Check the "Show Archived" option
+
+
+**Solution:**
+
+- Clear any active filters
+- Verify you're logged in as the right user
+- Check archived items
+
+### Slow Performance
+
+**Issue:** Generation or UI feels slower than expected
+
+**Possible Causes:**
+
+- Other users generating images simultaneously
+- Server resource limits
+- Network latency
+
+**Solutions:**
+
+- Check the queue to see if others are generating
+- Wait for current generations to complete
+- Contact administrator if persistent
+
+### Generation Stuck in Queue
+
+**Issue:** Your generation is queued but not starting
+
+**Possible Causes:**
+
+- Server is processing other users' generations
+- Server resources are fully utilized
+- Technical issue with the server
+
+**Solutions:**
+
+- Wait for your turn in the queue
+- Check if your generation is paused
+- Contact administrator if stuck for extended period
+
+---
+
+## Frequently Asked Questions
+
+### Can other users see my images?
+
+Not unless you change your board's visibility to "shared" or "public". All personal boards and images are private by default.
+
+### Can I share my workflows with others?
+
+Yes. You can mark any workflow as shared (public), which makes it visible to all users. Other users can view and use shared workflows, but only you or an administrator can modify or delete them.
+
+### How long do sessions last?
+
+- 24 hours by default
+- 7 days if you check "Remember me" during login
+
+### Can I use the API with multi-user mode?
+
+Yes, but you'll need to authenticate with a JWT token. See the [API Guide](./api-guide/) for details.
+
+### What happens if I forget my password?
+
+Contact your administrator. They can reset your password for you.
+
+### Can I have multiple sessions?
+
+Yes, you can log in from multiple devices or browsers simultaneously. All sessions will use the same account and see the same content.
+
+### Why can't I see the Model Manager "Add Models" tab?
+
+Regular users can see the Models tab but with read-only access. Check that you're logged in and try refreshing the page.
+
+### How do I know if I'm an administrator?
+
+Administrators see an "Admin" badge next to their name in the top-right corner and have access to additional features like User Management.
+
+### Can I request admin privileges?
+
+Yes, ask your current administrator to grant you admin privileges. Admin privileges will give you the ability to see all other users' boards and images, as well as to add models and change various server-wide settings.
+
+## Getting Help
+
+### Support Channels
+
+- **Administrator:** Contact your system administrator for account issues
+- **Documentation:** Check the [FAQ](/troubleshooting/faq/) for common issues
+- **Community:** Join the [Discord](https://discord.gg/ZmtBAhwWhy) for help
+- **Bug Reports:** File issues on [GitHub](https://github.com/invoke-ai/InvokeAI/issues)
+
+### Reporting Issues
+
+When reporting an issue, include:
+
+- Your role (regular user or administrator)
+- What you were trying to do
+- What happened instead
+- Any error messages you saw
+- Your browser and operating system
+
+## Additional Resources
+
+- [Administrator Guide](./admin-guide/) — For administrators managing users and the system
+- [API Guide](./api-guide/) — For developers using the InvokeAI API
diff --git a/docs/src/content/docs/workflows/adding-nodes.mdx b/docs/src/content/docs/features/Workflows/adding-nodes.mdx
similarity index 100%
rename from docs/src/content/docs/workflows/adding-nodes.mdx
rename to docs/src/content/docs/features/Workflows/adding-nodes.mdx
diff --git a/docs/src/content/docs/workflows/assets/groupsallscale.png b/docs/src/content/docs/features/Workflows/assets/groupsallscale.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/groupsallscale.png
rename to docs/src/content/docs/features/Workflows/assets/groupsallscale.png
diff --git a/docs/src/content/docs/workflows/assets/groupsconditioning.png b/docs/src/content/docs/features/Workflows/assets/groupsconditioning.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/groupsconditioning.png
rename to docs/src/content/docs/features/Workflows/assets/groupsconditioning.png
diff --git a/docs/src/content/docs/workflows/assets/groupscontrol.png b/docs/src/content/docs/features/Workflows/assets/groupscontrol.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/groupscontrol.png
rename to docs/src/content/docs/features/Workflows/assets/groupscontrol.png
diff --git a/docs/src/content/docs/workflows/assets/groupsimgvae.png b/docs/src/content/docs/features/Workflows/assets/groupsimgvae.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/groupsimgvae.png
rename to docs/src/content/docs/features/Workflows/assets/groupsimgvae.png
diff --git a/docs/src/content/docs/workflows/assets/groupsiterate.png b/docs/src/content/docs/features/Workflows/assets/groupsiterate.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/groupsiterate.png
rename to docs/src/content/docs/features/Workflows/assets/groupsiterate.png
diff --git a/docs/src/content/docs/workflows/assets/groupslora.png b/docs/src/content/docs/features/Workflows/assets/groupslora.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/groupslora.png
rename to docs/src/content/docs/features/Workflows/assets/groupslora.png
diff --git a/docs/src/content/docs/workflows/assets/groupsmultigenseeding.png b/docs/src/content/docs/features/Workflows/assets/groupsmultigenseeding.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/groupsmultigenseeding.png
rename to docs/src/content/docs/features/Workflows/assets/groupsmultigenseeding.png
diff --git a/docs/src/content/docs/workflows/assets/groupsnoise.png b/docs/src/content/docs/features/Workflows/assets/groupsnoise.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/groupsnoise.png
rename to docs/src/content/docs/features/Workflows/assets/groupsnoise.png
diff --git a/docs/src/content/docs/workflows/assets/linearview.png b/docs/src/content/docs/features/Workflows/assets/linearview.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/linearview.png
rename to docs/src/content/docs/features/Workflows/assets/linearview.png
diff --git a/docs/src/content/docs/workflows/assets/nodescontrol.png b/docs/src/content/docs/features/Workflows/assets/nodescontrol.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/nodescontrol.png
rename to docs/src/content/docs/features/Workflows/assets/nodescontrol.png
diff --git a/docs/src/content/docs/workflows/assets/nodesi2i.png b/docs/src/content/docs/features/Workflows/assets/nodesi2i.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/nodesi2i.png
rename to docs/src/content/docs/features/Workflows/assets/nodesi2i.png
diff --git a/docs/src/content/docs/workflows/assets/nodest2i.png b/docs/src/content/docs/features/Workflows/assets/nodest2i.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/nodest2i.png
rename to docs/src/content/docs/features/Workflows/assets/nodest2i.png
diff --git a/docs/src/content/docs/workflows/assets/workflow_library.png b/docs/src/content/docs/features/Workflows/assets/workflow_library.png
similarity index 100%
rename from docs/src/content/docs/workflows/assets/workflow_library.png
rename to docs/src/content/docs/features/Workflows/assets/workflow_library.png
diff --git a/docs/src/content/docs/workflows/comfyui-migration.mdx b/docs/src/content/docs/features/Workflows/comfyui-migration.mdx
similarity index 100%
rename from docs/src/content/docs/workflows/comfyui-migration.mdx
rename to docs/src/content/docs/features/Workflows/comfyui-migration.mdx
diff --git a/docs/src/content/docs/workflows/community-nodes.mdx b/docs/src/content/docs/features/Workflows/community-nodes.mdx
similarity index 100%
rename from docs/src/content/docs/workflows/community-nodes.mdx
rename to docs/src/content/docs/features/Workflows/community-nodes.mdx
diff --git a/docs/src/content/docs/workflows/editor-interface.mdx b/docs/src/content/docs/features/Workflows/editor-interface.mdx
similarity index 100%
rename from docs/src/content/docs/workflows/editor-interface.mdx
rename to docs/src/content/docs/features/Workflows/editor-interface.mdx
diff --git a/docs/src/content/docs/workflows/index.mdx b/docs/src/content/docs/features/Workflows/index.mdx
similarity index 100%
rename from docs/src/content/docs/workflows/index.mdx
rename to docs/src/content/docs/features/Workflows/index.mdx
diff --git a/docs/src/content/docs/features/gallery.mdx b/docs/src/content/docs/features/gallery.mdx
index fec8c918a3e..d5aad28188b 100644
--- a/docs/src/content/docs/features/gallery.mdx
+++ b/docs/src/content/docs/features/gallery.mdx
@@ -2,6 +2,8 @@
title: Gallery Panel
description: Learn how to manage, organize, and use your generated images and assets with the Gallery Panel in InvokeAI.
lastUpdated: 2026-02-19
+sidebar:
+ order: 1
---
import { Card, CardGrid, Steps } from '@astrojs/starlight/components';
diff --git a/docs/src/content/docs/features/hotkeys.mdx b/docs/src/content/docs/features/hotkeys.mdx
index 0c74b556b78..a4b99fca16d 100644
--- a/docs/src/content/docs/features/hotkeys.mdx
+++ b/docs/src/content/docs/features/hotkeys.mdx
@@ -2,6 +2,8 @@
title: Hotkeys
description: Learn how to use and customize hotkeys in InvokeAI, and how developers can interact with the hotkey system.
lastUpdated: 2026-02-19
+sidebar:
+ order: 2
---
import { Tabs, TabItem, Steps, Card, CardGrid, Icon } from '@astrojs/starlight/components';