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.
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';" warmdeskRestarting the server is optional after manual fixes like these.
Global roles
| Role | Capabilities |
|---|---|
| Full access to all projects, customers, users, and system settings |
| Can create projects, manage their own projects, access assigned resources |
| Read-only access to assigned projects and customers |
| Read-only access to the Prometheus |
| Can trigger and download backups via the API |
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)
Edit name, email, and global role
Deactivate or reactivate accounts; soft-deleted users can be restored or permanently purged
Reset a userβs MFA (TOTP)
View last login time and last password change
Create and revoke API keys on behalf of any user (useful for
metricsandbackupservice 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)
| Event | When it is recorded |
|---|---|
| Successful password or passkey login |
| Failed login attempt |
| Explicit logout |
| Silent access-token renewal |
| User changed their own password |
| User changed their email address |
| TOTP was enabled |
| TOTP was disabled by the user |
| WebAuthn passkey added |
| WebAuthn passkey removed |
| API key generated |
| API key revoked |
| An admin created this account |
| An admin edited this userβs profile or role |
| An admin soft-deleted this user |
| An admin restored this user from soft-delete |
| An admin permanently deleted this user |
| An admin reset this userβs MFA |
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.
Projects tab
View all projects, including archived ones
Archive, restore, or permanently delete projects
Change project ownership
System settings tab
See the System Settings section below.
Customers and contracts
Manage customer organisations under Admin β Customers.
Customer contracts are edited from the customer detail page (Customers β (click customer) β Contracts). Each contract has a base hourly rate and optional time slots that define alternative rates for work done outside standard hours β for example evening, overnight, or weekend standby rates.
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.
| Field | Description |
|---|---|
Label | Free-text name, e.g. |
From / To | Wall-clock window; overnight ranges are supported (e.g. |
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. |
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. 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.
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)
| Setting | Description |
|---|---|
SMTP host | Mail server hostname |
SMTP port | Usually 587 (STARTTLS) or 465 (TLS) |
SMTP username / password | Mail server credentials |
From address |
|
TLS mode |
|
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 (0 = no timeout)
Password max age β days before a password expires (0 = never); users with expired passwords are redirected to Settings on login
Group video calls (LiveKit)
WarmDesk supports group video calls in DM group conversations with 3+ members via LiveKit.
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"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
Company name β shown in the page title and emails
Company logo β PNG/SVG displayed on the login page and in the header
Locale defaults
Default language, date format, and timezone for new users. Users can override these in their own settings.
Password policy
| Setting | Description |
|---|---|
Minimum length | Default: 8 characters |
Max age (days) | Days before password expires; 0 disables expiry |
Bcrypt cost | Fixed at 12 (compile-time constant; change requires a rebuild) |
SMTP troubleshooting
Verify host, port, credentials, and TLS mode in System Settings
Use the Send test email button to confirm delivery
Check firewall rules for outbound port 587/465
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 ZIP containing the database dump and uploaded files. Download the ZIP for off-site storage. To restore, upload the ZIP and click Restore.
The Scheduled Backups section on the same tab lets you configure automatic backups: set the interval (hourly, daily, weekly), the retention count, and an optional download URL for off-site storage. Scheduled backups run server-side β no external cron job needed.
Warning | Restoring from backup replaces all current data. Take a fresh backup before restoring. |
Via the API (automation)
Use a personal API key with the backup role:
# Create a backup and download it
curl -H "X-API-Key: your-key" \
https://warmdesk.example.com/api/v1/admin/backup \
-o backup-$(date +%Y%m%d).zipTo schedule automatic backups, add a cron job (crontab -e):
# Every night at 02:00 β keep the last 30 days of backups
0 2 * * * curl -sS -H "X-API-Key: your-key" \
https://warmdesk.example.com/api/v1/admin/backup \
-o /backup/warmdesk-$(date +\%Y\%m\%d).zip && \
find /backup -name 'warmdesk-*.zip' -mtime +30 -deleteSQLite β manual backup
# Safe hot backup using SQLite's backup API
sqlite3 warmdesk.db ".backup /backup/warmdesk-$(date +%Y%m%d).db"Security hardening
Startup safety checks
WarmDesk refuses to start if:
jwt_secretis still the default valuechange-me-in-productiongin_modeisreleaseandallowed_originscontains*
Security checklist
β
jwt_secretis a long random stringβ
gin_mode: releasein productionβ
allowed_originslists only your actual domain(s)β
db_tls_mode: verify-fullfor remote databasesβ TLS terminated at the reverse proxy
β Port 8080 not exposed to the internet (firewall)
β
upload_diris outside the web rootβ Backups are scheduled and regularly tested
β MFA enforced for admin accounts
β Asciidoctor
safeModeset tounsafeonly in trusted deployments
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=requireRequirements:
All instances share the same PostgreSQL or MySQL database
Redis is required for WebSocket pub/sub across instances
Sticky sessions are not required β WebSocket connections are handled per-instance and bridged via Redis
Ansible deployment
An Ansible collection is available on Ansible Galaxy:
ansible-galaxy collection install ansilabnl.warmdeskThe collection installs the binary, creates a systemd unit, and configures nginx. See the collection README for variables and an example playbook.
Updating
Download the new binary
Stop the service:
systemctl stop warmdeskReplace the binary
Start the service:
systemctl start warmdesk
Schema migrations run automatically on startup via GORM AutoMigrate. No manual SQL migration steps are needed.
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 --resetPrometheus metrics
GET /api/v1/metrics exposes a standard Prometheus scrape endpoint.
Accessible to users with the metrics or admin global role.
Setup
In the admin panel Users tab, create a user with global role
metrics(e.g. usernameprometheus).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-keyNote | 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
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.