2026-06-19 18:46:59 +08:00
---
description : Manage users, built-in and custom roles, permissions, API keys, teams, sessions, and the audit log in SnapOtter.
---
2026-07-11 13:01:55 +08:00
# Users, Roles & Permissions {#users-roles-permissions}
2026-06-19 18:46:59 +08:00
SnapOtter ships three built-in roles, 17 granular permissions, and support for custom roles with optional per-tool access control. This page covers the full authorization model, API key scoping, team management, and audit logging.
::: tip Related pages
[OIDC / SSO ](/guide/oidc ) | [SAML SSO ](/guide/saml ) | [SCIM Provisioning ](/guide/scim ) | [Security & Hardening ](/guide/security )
:::
2026-07-11 13:01:55 +08:00
## Users {#users}
2026-06-19 18:46:59 +08:00
2026-07-11 13:01:55 +08:00
### Creating users {#creating-users}
2026-06-19 18:46:59 +08:00
2026-07-06 08:09:22 +08:00
Admins can create users through the admin panel or the `POST /api/auth/register` endpoint. Each user has a username, role, team assignment, and an optional email address.
2026-06-19 18:46:59 +08:00
2026-07-11 13:01:55 +08:00
### Default admin {#default-admin}
2026-06-19 18:46:59 +08:00
On first startup SnapOtter creates a default admin account. The credentials come from environment variables:
| Variable | Default | Description |
|---|---|---|
| `DEFAULT_USERNAME` | `admin` | Username for the initial admin account |
| `DEFAULT_PASSWORD` | `admin` | Password for the initial admin account |
The default admin is required to change their password on first login.
2026-07-11 13:01:55 +08:00
### Authentication providers {#authentication-providers}
2026-06-19 18:46:59 +08:00
Users can authenticate through several methods:
2026-06-24 12:06:31 +08:00
- **Local** - username and password stored in the SnapOtter database
- **OIDC** - any OpenID Connect provider (see [OIDC / SSO ](/guide/oidc ))
- **SAML** - SAML 2.0 identity providers (see [SAML SSO ](/guide/saml ))
- **SCIM** - automated provisioning from an identity provider (see [SCIM Provisioning ](/guide/scim ))
2026-06-19 18:46:59 +08:00
2026-07-11 13:01:55 +08:00
### Disabling authentication {#disabling-authentication}
2026-06-19 18:46:59 +08:00
Set `AUTH_ENABLED=false` to disable authentication entirely. In this mode a synthetic anonymous user with the `admin` role is used for all requests. No login is required.
::: warning
Disabling authentication grants full admin access to anyone who can reach the instance. Only use this in trusted environments.
:::
2026-07-11 13:01:55 +08:00
## Built-in roles {#built-in-roles}
2026-06-19 18:46:59 +08:00
SnapOtter includes three built-in roles. They cannot be modified or deleted.
2026-07-11 13:01:55 +08:00
### Admin {#admin}
2026-06-19 18:46:59 +08:00
All 17 permissions. Full control over the instance.
`tools:use` `files:own` `files:all` `apikeys:own` `apikeys:all` `pipelines:own` `pipelines:all` `settings:read` `settings:write` `users:manage` `teams:manage` `features:manage` `system:health` `audit:read` `compliance:manage` `webhooks:manage` `security:manage`
2026-07-11 13:01:55 +08:00
### Editor {#editor}
2026-06-19 18:46:59 +08:00
7 permissions. Can use all tools and manage all files and pipelines, but cannot access admin functions.
`tools:use` `files:own` `files:all` `apikeys:own` `pipelines:own` `pipelines:all` `settings:read`
2026-07-11 13:01:55 +08:00
### User {#user}
2026-06-19 18:46:59 +08:00
5 permissions. Can use tools and manage their own resources.
`tools:use` `files:own` `apikeys:own` `pipelines:own` `settings:read`
2026-07-11 13:01:55 +08:00
## Permissions reference {#permissions-reference}
2026-06-19 18:46:59 +08:00
| Permission | Description |
|---|---|
| `tools:use` | Use any processing tool |
| `files:own` | View and manage own files |
| `files:all` | View and manage all users' files |
| `apikeys:own` | Create and manage own API keys |
| `apikeys:all` | View all users' API keys |
| `pipelines:own` | Create and manage own pipelines |
| `pipelines:all` | View and manage all users' pipelines |
| `settings:read` | View instance settings |
| `settings:write` | Modify instance settings |
2026-07-22 01:23:15 +08:00
| `users:manage` | Create and manage user accounts within the actor's authority boundary |
2026-06-19 18:46:59 +08:00
| `teams:manage` | Create, update, and delete teams |
| `features:manage` | Install and manage AI feature bundles |
| `system:health` | Access health and readiness endpoints |
| `audit:read` | View the audit log and list roles |
2026-07-22 01:23:15 +08:00
| `compliance:manage` | Manage GDPR lifecycle and compliance features; destructive user operations remain authority-bounded |
2026-06-19 18:46:59 +08:00
| `webhooks:manage` | Configure outbound webhooks |
| `security:manage` | Manage security settings (IP allowlist, SSO enforcement) |
2026-07-11 13:01:55 +08:00
## Custom roles {#custom-roles}
2026-06-19 18:46:59 +08:00
2026-07-06 08:09:22 +08:00
Admins with the `security:manage` permission can create custom roles through the admin panel or the roles API. Listing roles requires `audit:read` .
2026-06-19 18:46:59 +08:00
2026-07-11 13:01:55 +08:00
### Creating a custom role {#creating-a-custom-role}
2026-06-19 18:46:59 +08:00
```bash
2026-07-06 08:09:22 +08:00
curl -X POST http://localhost:1349/api/v1/roles \
2026-06-19 18:46:59 +08:00
-H "Authorization: Bearer si_..." \
-H "Content-Type: application/json" \
-d '{
"name": "reviewer",
"description": "Can use tools and view all files",
"permissions": ["tools:use", "files:own", "files:all", "settings:read"]
}'
```
Role names must be 2-30 characters, lowercase alphanumeric with hyphens and underscores.
2026-07-22 01:23:15 +08:00
### Delegated administration boundaries {#delegated-administration-boundaries}
2026-06-19 18:46:59 +08:00
2026-07-22 01:23:15 +08:00
All 17 permissions can be delegated through custom roles, but an administrative permission does not make that role equivalent to the built-in `admin` role. User mutations authorized by `users:manage` , destructive operations authorized by `compliance:manage` , and custom-role management authorized by `security:manage` are bounded by the actor's current authority:
2026-06-19 18:46:59 +08:00
2026-07-22 01:23:15 +08:00
- Built-in roles follow `admin` > `editor` > `user` ; custom roles are below built-in roles.
- The target's permissions must be contained by the actor's **effective** permissions. A scoped API key therefore cannot exercise permissions omitted from its scope.
- A target role's tool access must be contained by the actor's own tool access.
- A disabled account is checked against its original role when that role is recorded as `disabled:<original-role>` .
- Deleting a custom role also requires authority to assign the built-in `user` fallback; disabled members remain disabled as `disabled:user` .
2026-06-19 18:46:59 +08:00
2026-07-22 01:23:15 +08:00
Global credentials and configuration are stricter: issuing or revoking the SCIM token and importing instance configuration require the built-in `admin` role with complete effective admin authority.
2026-06-19 18:46:59 +08:00
2026-07-11 13:01:55 +08:00
### Tool-level permissions {#tool-level-permissions}
2026-06-19 18:46:59 +08:00
Custom roles can optionally restrict which tools users may access. Two modes are available:
| Mode | Behavior | License requirement |
|---|---|---|
| `category` | Restrict by modality (image, video, audio, document, file) | None (free) |
| `tool` | Restrict by individual tool ID | Requires the `per_tool_permissions` enterprise feature |
When `tool` mode is set but the enterprise feature is not available, SnapOtter degrades gracefully and allows access to all tools.
```json
{
"name" : "image-only" ,
"permissions" : [ "tools:use" , "files:own" ],
"toolPermissions" : {
"mode" : "category" ,
"allowed" : [ "image" ]
}
}
```
2026-07-11 13:01:55 +08:00
### Deleting a custom role {#deleting-a-custom-role}
2026-06-19 18:46:59 +08:00
When a custom role is deleted, all users assigned to it are automatically reassigned to the `user` role.
2026-07-11 13:01:55 +08:00
## Teams {#teams}
2026-06-19 18:46:59 +08:00
Teams group users for storage and retention management. A `Default` team is created on first startup.
| Field | Type | Description |
|---|---|---|
| `name` | string | Unique team name (1-50 characters) |
| `storageQuota` | number | Per-team storage limit in bytes (works without enterprise) |
| `retentionHours` | number | Auto-delete outputs after this many hours (requires `team_retention_overrides` , enterprise) |
| `legalHold` | boolean | Prevent automatic deletion of team members' files (requires `legal_hold` , enterprise) |
::: info
The `Default` team cannot be deleted. Teams that still have members cannot be deleted. Reassign members first.
:::
2026-07-11 13:01:55 +08:00
## API keys {#api-keys}
2026-06-19 18:46:59 +08:00
Users can generate API keys for programmatic access. Each key uses the `si_` prefix and is shown only once at creation time.
2026-07-11 13:01:55 +08:00
### Scoped permissions {#scoped-permissions}
2026-06-19 18:46:59 +08:00
API keys can optionally carry a `permissions` array. When set, the effective permissions for a request are the **intersection** of the user's role permissions and the key's scoped permissions. This means an API key can never escalate beyond the user's own permissions.
```bash
2026-07-06 08:09:22 +08:00
curl -X POST http://localhost:1349/api/v1/api-keys \
2026-06-19 18:46:59 +08:00
-H "Authorization: Bearer si_..." \
-H "Content-Type: application/json" \
-d '{
"name": "CI pipeline key",
"permissions": ["tools:use", "files:own"],
"expiresAt": "2027-01-01T00:00:00Z"
}'
```
2026-07-11 13:01:55 +08:00
### Expiration {#expiration}
2026-06-19 18:46:59 +08:00
Keys accept an optional `expiresAt` timestamp. Expired keys are rejected at authentication time.
2026-07-11 13:01:55 +08:00
## Audit log {#audit-log}
2026-06-19 18:46:59 +08:00
SnapOtter records security-relevant events in a structured audit log stored in the `audit_log` database table.
2026-07-11 13:01:55 +08:00
### Viewing the audit log {#viewing-the-audit-log}
2026-06-19 18:46:59 +08:00
```
GET /api/v1/audit-log?page=1&limit=50&action=LOGIN_FAILED&from=2026-01-01T00:00:00Z&to=2026-12-31T23:59:59Z
```
Requires the `audit:read` permission. Supports pagination (`page` , `limit` ) and filters (`action` , `ip` , `from` , `to` ).
2026-07-11 13:01:55 +08:00
### Tool operation auditing {#tool-operation-auditing}
2026-06-19 18:46:59 +08:00
::: warning
`TOOL_EXECUTED` events are **not** logged by default. They are opt-in through either of two paths:
1. Set the `auditToolOperations` admin setting to `true` .
2. Hold an active license with the `audit_export` feature (available on both team and enterprise plans).
Without one of these, individual tool executions are not recorded in the audit log.
:::
2026-07-11 13:01:55 +08:00
### Exporting {#exporting}
2026-06-19 18:46:59 +08:00
```
GET /api/v1/enterprise/audit/export?format=csv&from=2026-01-01T00:00:00Z
```
Requires the `audit:read` permission and the `audit_export` enterprise feature (available on both team and enterprise plans). Supports CSV and JSON formats, filtered by `action` , `actorId` , `targetType` , `targetId` , `from` , and `to` .
2026-07-11 13:01:55 +08:00
### Tamper-resistant signing {#tamper-resistant-signing}
2026-06-19 18:46:59 +08:00
When enabled, each audit log entry is signed with an HMAC derived from `DATA_ENCRYPTION_KEY` . This requires:
1. Setting `DATA_ENCRYPTION_KEY` in your environment.
2. Enabling the `tamperResistantAudit` admin setting.
3. An enterprise license with the `tamper_resistant_audit` feature.
2026-07-11 13:01:55 +08:00
### Retention {#retention}
2026-06-19 18:46:59 +08:00
Set `AUDIT_RETENTION_DAYS` to automatically purge old entries. The default is `0` , which means entries are kept indefinitely.
2026-07-11 13:01:55 +08:00
### Event reference {#event-reference}
2026-06-19 18:46:59 +08:00
| Event | Category |
|---|---|
| `LOGIN_SUCCESS` , `LOGIN_FAILED` | Authentication |
| `OIDC_LOGIN_SUCCESS` , `OIDC_LOGIN_FAILED` | Authentication |
| `SAML_LOGIN_SUCCESS` , `SAML_LOGIN_FAILED` | Authentication |
| `LOGOUT` | Authentication |
| `USER_CREATED` , `USER_UPDATED` , `USER_DELETED` | User management |
| `PASSWORD_CHANGED` , `PASSWORD_RESET` | User management |
| `MFA_ENROLLED` , `MFA_DISABLED` , `MFA_VERIFIED` , `MFA_VERIFY_FAILED` | MFA |
| `MFA_CHALLENGE_ISSUED` , `MFA_RECOVERY_USED` , `MFA_RESET` | MFA |
| `ROLE_CREATED` , `ROLE_UPDATED` , `ROLE_DELETED` | Roles |
| `API_KEY_CREATED` , `API_KEY_DELETED` | API keys |
| `SETTINGS_UPDATED` , `IP_ALLOWLIST_UPDATED` | Settings |
| `FILE_UPLOADED` , `FILE_DELETED` | Files |
| `TOOL_EXECUTED` | Tools (opt-in) |
| `SCIM_USER_PROVISIONED` , `SCIM_USER_UPDATED` , `SCIM_USER_DEPROVISIONED` | SCIM |
| `SCIM_GROUP_SYNCED` | SCIM |
| `LEGAL_HOLD_APPLIED` , `LEGAL_HOLD_RELEASED` | Compliance |
| `GDPR_EXPORT_INITIATED` , `GDPR_USER_PURGED` , `GDPR_TEAM_PURGED` | Compliance |
| `CONFIG_EXPORTED` , `CONFIG_IMPORTED` | Configuration |
2026-07-11 13:01:55 +08:00
## Session management {#session-management}
2026-06-19 18:46:59 +08:00
Sessions are cookie-based, controlled by `SESSION_DURATION_HOURS` (default: 168 hours / 7 days).
2026-07-11 13:01:55 +08:00
### Role changes invalidate sessions {#role-changes-invalidate-sessions}
2026-06-19 18:46:59 +08:00
When an admin changes a user's role, all of that user's active sessions are deleted. The user must log in again to pick up their new permissions.
2026-07-11 13:01:55 +08:00
### Safety guards {#safety-guards}
2026-06-19 18:46:59 +08:00
- **Last-admin protection**: the last remaining admin cannot be demoted to a lower role. The API returns an error if you try.
- **Self-delete prevention**: admins cannot delete their own account through the API.