Administrator Guide

Installing, configuring, and operating WarmDesk as an administrator


See the Installation Guide for binary download, database setup, systemd, and reverse proxy instructions. This guide focuses on configuration and day-to-day administration.

First registered user (automatic admin)

When the user table is empty, the account created by the first successful self-registration is given the admin global role automatically—no SQL or manual promotion step.

Additional administrators can be assigned later from Admin panel → Users. If public registration is not wanted, turn it off after creating the first admin (Admin → Settings → Allow public registration).

Recovering admin access

If every administrator has been demoted or deactivated and you are locked out, restore access with a direct database update (use the account’s username, not its numeric id):

# SQLite
sqlite3 warmdesk.db "UPDATE users SET global_role='admin' WHERE username='yourname';"

# PostgreSQL
psql -U warmdesk -c "UPDATE users SET global_role='admin' WHERE username='yourname';"

# MySQL (adjust user and database name to match `db_dsn` in warmdesk.yaml)
mysql -u warmdesk -p -e "UPDATE users SET global_role='admin' WHERE username='yourname';" warmdesk

Restarting the server is optional after manual fixes like these.

Server modes

Two optional settings change what a WarmDesk server offers or how it looks. Each can be set as a YAML key, an environment variable, or a CLI flag (the flag wins over the YAML key, which wins over the environment variable).

YAML keyEnv varCLI flagEffect

app_mode: timetracking

APP_MODE

--mode=timetracking

Runs a time-tracking-only server: board, chat, topic, and helpdesk routes are disabled, related admin settings are hidden, and the time-tracking logos replace the default WarmDesk logos in the web UI and PDFs.

app_mode: ticketsystem

APP_MODE

--mode=ticketsystem

Runs a helpdesk-only server: board, chat, and time-tracking routes are disabled, related menus and admin sections are hidden, and the ticket logos replace the default WarmDesk logos in the web UI and PDFs.

app_mode: board

APP_MODE

--mode=board

Runs a board-only server: time-tracking and helpdesk routes are disabled and related menus and admin sections are hidden, while boards, topics, and chat stay. The default WarmDesk logos are kept.

instance_mode: test

INSTANCE_MODE

--instance-mode=test

Marks a test or staging server: orange, "TEST"-ribboned logos are served everywhere (web UI, PDFs, and the desktop app’s tray icon), so it is never mistaken for production.

Both default to empty (full WarmDesk, production) and combine independently — a timetracking + test server shows the orange time-tracking logo. A custom company logo (Admin → Settings → Branding & Billing) still takes precedence in PDFs. GET /api/v1/version reports both values so clients can detect them.

Global roles

RoleCapabilities

admin

Full access to all projects, customers, users, and system settings

user

Uses the application and sees assigned projects and customers; can create projects only when granted the Create projects permission

viewer

Read-only access to assigned projects and customers

metrics

Read-only access to the Prometheus /api/v1/metrics endpoint

backup

Can only trigger backups via POST /api/v1/backup

customer

Customer-portal access: view and comment on tickets of assigned customers; blocked from boards, chat, and time tracking

Within each project, users have a project role (owner, member, viewer) that is independent of their global role. Admin users bypass all project-level checks.

Admin panel

Access the admin panel from the sidebar (admin users only). It contains:

Users tab

  • Create new user accounts (for when self-registration is disabled), optionally requiring a password change on next login

  • Edit name, email, and global role

  • Grant per-user features in the Feature Access table — including Create projects (off by default for non-admins; admins can always create projects) and Helpdesk (tickets)

  • Deactivate or reactivate accounts; soft-deleted users can be restored or permanently purged

  • Reset a user’s MFA (TOTP) or revoke all of their passkeys (the Passkey column shows who has one)

  • View last login time and last password change

  • Create and revoke API keys on behalf of any user (useful for metrics and backup service accounts)

  • View the full Login History for each user — every authentication and security-sensitive event with IP, client, timestamp, and the actor (blank for self-actions; admin username for admin-on-behalf actions)

Table 1. Events recorded in Login History
EventWhen it is recorded

login_ok

Successful password or passkey login

login_failed

Failed login attempt (wrong password, unknown or deactivated user, failed passkey)

logout

Explicit logout

password_changed

User changed their own password

password_reset

Password reset completed via the emailed link

email_changed

User changed their email address

mfa_challenge

An MFA challenge was issued after a successful password/passkey check

mfa_ok

MFA challenge passed

mfa_failed

Invalid MFA code, or the MFA session expired

mfa_trusted_device

User chose to skip MFA on this device for 7 or 30 days

mfa_disabled

TOTP was disabled by the user

passkey_registered

WebAuthn passkey added

passkey_deleted

WebAuthn passkey removed

api_key_created

API key generated

api_key_deleted

API key revoked

admin_user_created

An admin created this account

admin_user_updated

An admin edited this user’s profile or role

admin_user_deleted

An admin soft-deleted this user

admin_user_restored

An admin restored this user from soft-delete

admin_user_purged

An admin permanently deleted this user

admin_mfa_disabled

An admin reset this user’s MFA

admin_passkeys_revoked

An admin revoked all of this user’s passkeys

Groups tab

Define user groups and assign them to projects and customers. Assigning a group to a project gives all group members the specified project role. This is useful for onboarding an entire team at once. Group access is additive: a user’s effective role is the highest one from direct and group assignments.

Note
Non-admin users only see customers they are explicitly assigned to (directly or via a group). The ticket assignee dropdown lists only direct customer assignments, so give every assignable agent a direct Customer Access row.

Projects tab

  • View all projects, including archived and closed ones (Show closed toggle)

  • Create projects, and archive, close/reopen, restore, or permanently delete them

  • Change project ownership

A project’s card prefix (e.g. PRJ in PRJ-42) must be unique and cannot be changed after creation.

System settings tab

See the System Settings section below.

Customers and contracts

Manage customer organisations under Admin → Customers (name, description, logo, and an optional colour used by the time-tracking calendar).

Each customer detail page (Customers → (click customer)) also holds:

  • Contracts — base hourly rate and optional time slots (see below)

  • Billing information — billing address, VAT number, and PO reference, printed on invoices

  • Contacts — informational contact persons (not user accounts)

  • Locations — named sites with address, contact details, and a standard travel distance; picking a location in time tracking auto-fills the entry’s distance (the value stays editable)

  • Invoices — generated from contract-linked time entries (draft → sent → paid, plus credit notes), with a PDF using the company details from Admin → Settings → Branding & Billing

Reusable invoice starting points (line items, VAT rate, currency, notes) are managed under Admin → Invoice Templates.

To see all standby slots across every customer in one place, click the ₡ Contract Rates button in the Customers page header or in the Time Tracking toolbar. The modal groups slots by customer → contract and shows from/to times, applicable days, and the rate or multiplier for each slot.

Table 2. Contract time slot fields
FieldDescription

Label

Free-text name, e.g. Standby - week or Weekend surcharge

From / To

Wall-clock window; overnight ranges are supported (e.g. 19:00 – 07:00)

Days

Which days apply: All days, Weekdays, Weekends, or a specific weekday

Ends after

For overnight slots — how many calendar days later the end time falls. Use Next day for Mon–Fri 19:00–07:00; 3 days later for a Fri 19:00–Mon 07:00 weekend shift.

Factor

Multiplies the contract base hourly rate (e.g. 1.5 = 150 %). Leave blank to use only the flat Rate field.

Rate

Flat hourly rate for this slot, overriding the contract base rate. If both Factor and Rate are set, the rate is also multiplied.

A contract can carry any number of slots. The PDF time report renders a sub-row per slot for each entry that overlaps the window, showing billable hours broken down by rate.

Time tracking

Time-tracking projects and customers are managed from Admin → Time Tracking or from the ⚙ gear button on the time-tracking page (create, rename, recolour, set undeclarable minutes, delete). Admin-created items are visible to all users and labelled Global (created by admin) in the management lists.

Standby shift entry

Every project row on the weekly time sheet has a ⏳ standby shift button (visible on hover/focus). It opens a dialog where you enter a start date + time and an end date + time. WarmDesk splits the range into individual per-day entries and places them in the correct day columns automatically. A Weekend standby preset button fills in Friday 19:00 → Monday 07:00 for the current week.

If the project’s contract has time slots configured, the PDF report will break the generated entries down by slot rate automatically.

Helpdesk

  • Enable helpdesk access per user (Admin → People & access → Users → Edit → Helpdesk (tickets)); admins always have access.

  • SLA policies, Macros, and Checklist templates are in the Helpdesk group of the admin menu. An SLA policy can filter on priority, type, customer, and category (the most specific match wins), count in a business-hours calendar, warn at a threshold, and escalate to a group or user on breach.

  • Admin → Helpdesk → Ticket settings holds the impact × urgency priority matrix, the auto-close period for resolved tickets, whether a resolution code is required, the major-incident group, the ticket categories, and the resolution codes.

  • Admin → Helpdesk → Business hours defines working calendars with holidays; Add holidays adds a country’s public holidays for a year, like the button in time tracking.

  • Admin → Helpdesk → Service catalogue defines orderable services: request form, fulfilment group, task checklist, and approver group with the number of approvals needed.

  • Admin → Change & configuration → Change management sets the change advisory board group and the approvals needed for normal and emergency changes, and manages freeze windows.

  • Admin → Change & configuration → CMDB types manages the configuration item types.

  • Incoming Mail (IMAP) polls a mailbox and turns new mail into tickets; processed messages are moved to a Processed folder (or marked \Seen if the server lacks MOVE). Plain and OAuth2 (Google / Office 365) authentication are supported — OAuth2 client IDs and secrets go in the oauth2: block of warmdesk.yaml and need a restart.

System settings

System settings are stored as key/value rows in the database and take effect immediately without a restart. Configure them from Admin → System Settings.

SMTP (email)

SettingDescription

SMTP host

Mail server hostname (leave empty to disable outgoing mail)

SMTP port

Usually 587 (STARTTLS, recommended); 25 for an internal relay

SMTP username / password

Mail server credentials; leave empty for an auth-less relay

From address / name

From: header on all outgoing mail

There is no separate TLS setting: STARTTLS is used automatically when the mail server offers it. Initial values can also come from the smtp: block in warmdesk.yaml; the admin panel overrides them. Test the SMTP configuration with the Send test email button.

Registration and sessions

  • Allow public registration — enable or disable self-registration on the login page

  • Session timeout — idle minutes before a user is logged out (default 60; 0 = no timeout)

  • Maximum password age — days after which users are asked to change their password (0 = never, the default)

Security

  • Require MFA for all users — users are prompted to set up TOTP after their next login

  • Remember MFA on trusted devices — Disabled, 1 week only, or 1 week or 1 month (default); tightening it revokes existing trusted devices that no longer fit

  • API allowed IP addresses — comma-separated IPs/CIDR ranges allowed to use API keys (empty = no restriction). Browser sessions are never blocked. A wrong value locks all API-key clients out.

Calls (LiveKit and TURN)

WarmDesk supports group video calls in DM group conversations with 3+ members via LiveKit. One-to-one calls use WebRTC directly; add a TURN server so they also work across NAT, CGNAT, and restrictive firewalls (without one, such calls connect but show no audio/video).

Add these settings in warmdesk.yaml (or environment variables) and restart the server:

livekit_url: "wss://livekit.example.com"
livekit_api_key: "APIxxxxxxxxxxxxxxxx"
livekit_api_secret: "your-secret"
livekit_room_prefix: ""        # optional, when several servers share one LiveKit

turn_urls: "turn:turn.example.com:3478"   # comma-separated list allowed
turn_username: "warmdesk"
turn_credential: "your-secret"

When LiveKit is not configured, one-on-one WebRTC calls continue to work and the UI shows a status banner in group chats explaining that group video is unavailable on that server.

Branding

Set these under Admin → Settings → Branding & Billing:

  • Company name — shown in the login screen branding panel and in report headers / PDF footers

  • Company logo — separate logos for light and dark backgrounds (JPG, PNG, GIF, WebP, or SVG), used on the login screen, reports, and PDFs

  • Show company branding on the login screen — splits the login page into a branding panel and the login form

  • Billing — company address, VAT and CoC numbers, IBAN/BIC, payment terms, default VAT rate, invoice number prefix, and a VAT-exempt toggle, all used on invoice PDFs

New project defaults and features

  • Initial columns and Initial labels — created for every new project (existing projects are not changed)

  • Scrum Story Points — shows a story-points field on cards

Locale defaults

Default language, date format, timezone, theme, and font for new users. Users can override these in their own settings.

Password policy

Configure under Admin → Settings → Password Policy. The policy applies to registration, password changes, and email resets.

SettingDescription

Minimum length

Default: 12 characters; cannot be set below 8

Character classes

Require uppercase, lowercase, digit, and/or special characters (lowercase and digit are on by default)

Maximum password age

Days before users are asked to change their password; 0 disables expiry

Bcrypt cost

Fixed at 12 (compile-time constant; change requires a rebuild)

Caution
A password set by an admin in Admin → Users → Create User is only checked against an 8-character minimum, not the policy. Use Require password change on next login if you rely on the policy.

SMTP troubleshooting

  • Verify host, port, and credentials in System Settings

  • Use the Send test email button to confirm delivery

  • Check firewall rules for outbound port 587

  • If using Gmail, create an App Password (regular passwords are blocked)

Backup and recovery

Via the admin panel

Go to Admin → Backup / Restore. Click Create Backup to generate a warmdesk_backup_<timestamp>_<hex>.tar.gz archive in ./backups/ containing the database dump (under db/) and, by default, the upload directory (attachments, avatars, customer logos, company branding, under uploads/) — uncheck Include uploaded files in the scheduled-backup settings for database-only backups. Download it for off-site storage. Older database-only backups (warmdesk_db_*.db / .sql) are still listed and restorable.

Restore asks how to apply a backup:

  • Completely restart (replace) — replaces the database and the upload directory with the backup’s contents. SQLite restores are live; no server restart needed.

  • Add to current data (merge) — uploaded files are copied in alongside existing ones; database rows are added only when their ID doesn’t already exist locally (SQLite only — on PostgreSQL/MySQL only the uploads are merged).

To bring a backup file from another WarmDesk server into this one, click Import Backup and choose a .tar.gz, .db, or .sql file. It is saved into ./backups/ and you are asked the same replace-or-merge question.

The scheduling section on the same tab lets you configure automatic backups: interval (every 6/8/12/24 hours), an optional time-of-day anchor, the retention count (default 10), and an optional notification email (several addresses may be separated by commas, semicolons, or spaces). Scheduled backups run server-side — no external cron job needed.

Warning
Completely restart replaces all current data with the backup’s contents. Add to current data never overwrites existing rows, but a row whose ID collides with unrelated existing data is skipped rather than merged — two independent servers share no ID space.

Via the API (automation)

Use an API key belonging to a user with the backup role (or an admin). Send it in the X-API-Key header:

# Create a backup; the JSON response contains the new filename
curl -H "X-API-Key: your-key" -X POST \
     https://warmdesk.example.com/api/v1/backup

The backup role can only trigger backups. Downloading an archive (GET /api/v1/admin/system/backups/<filename>) requires an admin — or copy it straight from ./backups/ on the server.

To schedule automatic backups outside WarmDesk’s own scheduler, add a cron job (crontab -e):

# Every night at 02:00
0 2 * * * curl -sS -X POST -H "X-API-Key: your-key" \
               https://warmdesk.example.com/api/v1/backup

SQLite — manual backup

# Safe hot backup using SQLite's backup API
sqlite3 warmdesk.db ".backup /backup/warmdesk-$(date +%Y%m%d).db"

Restoring onto a different database driver

The admin panel only restores within the same driver. warmdesk-db-convert (shipped alongside warmdesk) copies all data between any combination of SQLite, PostgreSQL, and MySQL — directly from a backup archive, including its bundled uploads:

./warmdesk-db-convert \
  --backup ~/warmdesk_backup_20260416_1430_a3f9.tar.gz \
  --dst-config ./warmdesk.yaml \
  --truncate

--dst-config takes the destination’s database and upload_dir settings from its own config file; --truncate empties destination tables first so the command can be re-run. Run it without arguments for all flags (including direct database-to-database conversion).

Security hardening

Startup safety checks

WarmDesk refuses to start if:

  • jwt_secret is still the default value change-me-in-production

  • jwt_secret is shorter than 32 characters

  • gin_mode is release and allowed_origins contains *

  • db_driver is mysql and db_dsn lacks parseTime=true (without it every timestamp read silently fails, showing up as misleading "invalid credentials" / "not found" errors)

  • app_mode or instance_mode has an unknown value

The supplied systemd unit (deploy/warmdesk.service) uses Type=notify: the server signals readiness only after it has bound its port, so systemctl start warmdesk reports a failed start (bad secret, port in use, …) immediately instead of claiming success. A warning is logged when a remote database is used without TLS.

Security checklist

  • ❏ jwt_secret is a long random string (e.g. openssl rand -hex 32)

  • ❏ gin_mode: release in production

  • ❏ allowed_origins lists only your actual domain(s)

  • ❏ db_tls_mode: verify-full for remote databases

  • ❏ TLS terminated at the reverse proxy

  • ❏ Port 8080 not exposed to the internet (firewall)

  • ❏ Service runs as a dedicated non-root user

  • ❏ upload_dir is outside the web root

  • ❏ Backups are scheduled and regularly tested

  • ❏ MFA enforced for admin accounts

  • ❏ Password policy set to at least 12 characters with character-class requirements

  • ❏ Public registration disabled if only known users should have access

  • ❏ API-key access restricted with API allowed IP addresses where possible

Known limitations

  • The in-process rate limiter is per-instance. In a multi-instance deployment, each instance has its own counter — a distributed brute-force attack can bypass the limit. Use a WAF or rate-limiting proxy for multi-instance deployments.

  • In the desktop (Tauri) app, JWT tokens are stored in sessionStorage (accessible to JavaScript). A proper fix requires routing API calls through the Rust layer — not yet implemented.

Horizontal scaling

Run multiple WarmDesk instances behind a load balancer:

redis_url: redis://redis-host:6379/0
db_driver: postgres
db_dsn: host=pg-primary user=warmdesk password=secret dbname=warmdesk sslmode=require

Requirements:

  • All instances share the same PostgreSQL or MySQL database (not SQLite)

  • Redis is required for WebSocket pub/sub across instances

  • All instances mount the same shared upload_dir (e.g. NFS) and use the same jwt_secret

  • Sticky sessions are not required — WebSocket connections are handled per-instance and bridged via Redis

  • Enable scheduled backups on exactly one instance, to avoid overlapping runs

Ready-made templates (identical warmdesk.yaml, systemd unit, nginx load balancer config, NFS fstab example, and setup notes) are in deploy/multi-instance/.

Ansible deployment

An Ansible collection is available on Ansible Galaxy:

ansible-galaxy collection install ansilabnl.warmdesk

The collection installs the binary, creates a systemd unit, and configures nginx. It also provides modules for managing WarmDesk content (users, groups, customers, contracts, projects, cards, and more) through the API. See the collection README for variables and an example playbook.

Updating

For tarball installs, run the bundled get_warmdesk script as root (sudo get_warmdesk, or --force to reinstall): it downloads the latest release for your architecture, stops the service, unpacks it, and restarts it.

Manually:

  1. Download the new binary

  2. Stop the service: systemctl stop warmdesk

  3. Replace the binary

  4. Start the service: systemctl start warmdesk

Schema migrations run automatically on startup via GORM AutoMigrate. No manual SQL migration steps are needed.

Desktop clients on Linux (.deb, .rpm, AppImage) and macOS can be updated with the deploy/update_warmdesk_client script, which verifies the GPG signature of the download.

Maintenance and migration tools

ToolPurpose

warmdesk-db-convert

Copy data between SQLite, PostgreSQL, and MySQL, or restore a backup onto another driver (see above)

warmdesk-import / warmdesk-export

Move projects from/to Jira, Trello, OpenProject, or Ryver (Ryver imports also bring chat history). Configure with warmdesk-migrate.yaml; user_map attributes imported cards, comments, and messages to the right WarmDesk accounts; --dry-run previews

warmdesk-timer

Command-line time-tracking timer for users: start, stop, status, cancel, projects. Runs on the user’s own machine with a personal API key; installed by the desktop .deb/.rpm packages as /usr/bin/warmdesk-timer, and published standalone for Linux, macOS, and Windows on the release page (see the user guide)

warmdesk-training

Creates (or --reset removes) isolated training slots — one user, customer, contract, and project each

gc-uploads

Lists files in upload_dir no longer referenced by any user, customer, project, group, setting, or attachment (dry run by default); --delete removes them. Run from the source tree: cd backend && go run ./cmd/gc-uploads [--config warmdesk.yaml] [--delete]

Demo data

The warmdesk-seed binary (shipped alongside the main server) populates the database with sample projects, cards, users, and more for demonstration purposes. It is idempotent — it exits early if demo data is already present (detected by the demo.admin username).

# Basic usage
./warmdesk-seed

# Override the config file location
./warmdesk-seed --config /path/to/warmdesk.yaml

# Drop all existing demo data first, then re-seed
./warmdesk-seed --reset

Prometheus metrics

GET /api/v1/metrics exposes a standard Prometheus scrape endpoint. Accessible to users with the metrics or admin global role.

Setup

  1. In the admin panel Users tab, create a user with global role metrics (e.g. username prometheus).

  2. Open the edit modal for that user, scroll to API Keys, and click Generate to create a key. Copy the key — it is only shown once.

Prometheus scrape config

WarmDesk accepts the API key via an Authorization: ApiKey <key> header. Use Prometheus' built-in authorization block (no proxy required):

scrape_configs:
  - job_name: warmdesk
    static_configs:
      - targets:
          - warmdesk.example.com      # no port needed for standard HTTPS/HTTP
    metrics_path: /api/v1/metrics
    scheme: https                     # or http
    authorization:
      type: ApiKey
      credentials: your-metrics-api-key
Note
bearer_token / Authorization: Bearer is not supported for API key authentication — it is reserved for JWT tokens. Use authorization.type: ApiKey as shown above.

Exposed metrics

Metrics cover projects, columns, and cards; users by role; customers; tickets by status and priority, SLA breaches, and ticket messages; and backup health (warmdesk_backup_last_run_timestamp_seconds, warmdesk_backup_last_success, warmdesk_backup_files_total). A ready-made Grafana dashboard ships as docs/grafana-dashboard.json.

The endpoint also exposes two self-monitoring gauges:

  • warmdesk_metrics_last_access_timestamp_seconds — Unix timestamp of the previous successful scrape (0 if never).

  • warmdesk_metrics_last_access_success — result of the previous scrape: 1 = success, 0 = failed (auth error), -1 = never scraped.

These are visible in Admin → Settings → Metrics access log.