WarmDesk uses GORM with automatic migration (AutoMigrate) β there are no separate migration files.
Every time the server starts it compares the model structs to the live schema and adds any missing columns or tables.
You should never need to touch the database directly for routine upgrades.
Supported engines: SQLite (default), PostgreSQL, and MySQL.
Design decisions
Fractional position ordering
Columns and cards use a position REAL (float) column.
Inserting between two items assigns the midpoint of the surrounding values.
This avoids renumbering the entire list on every drag-and-drop.
Atomic card numbering
Each project has a card_counter INTEGER column.
The backend increments it atomically when creating a card and uses the result as the human-readable number (e.g. PRJ-42).
Polymorphic attachments
The attachments table stores files for cards, card comments, chat messages, and direct messages through two columns: owner_type (string) and owner_id (integer).
Valid owner types: card, card_comment, chat_message, conv_message.
System settings
Operational settings (SMTP, branding, locale defaults, session timeout, β¦) live in a system_settings key/value table and are read at request time so changes take effect without a restart.
Core tables
users
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | GORM auto-field | |
| TIMESTAMP | GORM auto-field | |
| TIMESTAMP | nullable, index | Soft-delete support |
| VARCHAR | unique, not null | Login identifier |
| VARCHAR | unique, not null | Display name |
| VARCHAR | not null | bcrypt, cost 12 |
| VARCHAR | default |
|
| VARCHAR | default | UI language preference |
| VARCHAR | default |
|
| VARCHAR | nullable | Profile picture path |
| BOOLEAN | default false | MFA active flag |
| VARCHAR | nullable | Encrypted TOTP secret |
| BOOLEAN | default true | Account enabled |
| BOOLEAN | default false | Force password reset on next login |
projects
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| TIMESTAMP | nullable, index | |
| VARCHAR | not null | Display name |
| VARCHAR | unique, not null | URL-safe identifier used in all API routes |
| TEXT | nullable | |
| VARCHAR | not null | Prefix for card numbers (e.g. |
| INTEGER | default 0 | Atomic counter, incremented per card |
| VARCHAR | default |
|
| BIGINT | FK β customers, nullable | Owning customer |
| VARCHAR | nullable | Hex accent colour for the project card |
| BOOLEAN | default false |
project_members
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β projects | |
| BIGINT | FK β users | |
| VARCHAR | default |
|
Board tables
columns
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| BIGINT | FK β projects | |
| VARCHAR | not null | |
| REAL | not null | Fractional ordering |
| INTEGER | default 0 | 0 = unlimited |
| VARCHAR | nullable | Column header accent |
cards
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| TIMESTAMP | nullable, index | |
| BIGINT | FK β columns | Current column |
| BIGINT | FK β projects | |
| VARCHAR | not null | |
| TEXT | nullable | Markdown body |
| REAL | not null | Fractional ordering within column |
| VARCHAR | default |
|
| VARCHAR | default |
|
| DATE | nullable | |
| REAL | default 0 | |
| INTEGER | not null | Human-readable number (PRJ-N) |
| INTEGER | default 0 | Scrum only |
| BIGINT | FK β cards, nullable | Sub-card relationship |
| BIGINT | FK β users, nullable | Who created the card |
card_assignees
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β cards | |
| BIGINT | FK β users |
card_checklist_items
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| BIGINT | FK β cards | |
| VARCHAR | not null | |
| BOOLEAN | default false | |
| REAL | not null | Fractional ordering |
card_comments
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| TIMESTAMP | nullable, index | |
| BIGINT | FK β cards | |
| BIGINT | FK β users | |
| TEXT | not null | Markdown |
labels
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β projects | |
| VARCHAR | not null | |
| VARCHAR | not null | Hex colour |
card_labels
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β cards | |
| BIGINT | FK β labels |
card_references
Cross-references between cards (e.g. "blocks", "is blocked by", "relates to").
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β cards | Source card |
| BIGINT | FK β cards | Referenced card |
| VARCHAR | not null |
|
card_history
Audit log for card changes.
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| BIGINT | FK β cards | |
| BIGINT | FK β users | |
| VARCHAR | not null | Changed field name |
| TEXT | nullable | |
| TEXT | nullable |
Customer & contract tables
customers
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| VARCHAR(200) | not null | |
| TEXT | nullable | |
| VARCHAR(500) | nullable | Path to uploaded logo |
| INTEGER | default 0 | Display order in the customer list |
| BOOLEAN | default false | Hides from helpdesk/board views; visible in time-tracking only |
| BIGINT | FK β users, nullable, index | User who created the record (nil = system / seeded) |
contracts
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| BIGINT | FK β customers, not null, index | |
| VARCHAR(200) | not null | |
| TEXT | nullable | |
| TIMESTAMP | nullable | |
| TIMESTAMP | nullable | |
| REAL | nullable | Base hourly rate |
| VARCHAR(3) | default | Displayed alongside monetary values |
contract_time_slots
Defines an alternative rate window on a contract β for example a standby/on-call surcharge outside regular hours.
When end_time < start_time the slot crosses midnight; end_day_offset says how many calendar days later the end time falls (1 = next morning, 3 = Monday morning for a FridayβMonday weekend slot).
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β contracts, not null, index | |
| VARCHAR(100) | nullable | Free-text name, e.g. |
| VARCHAR(5) | not null | Wall-clock |
| VARCHAR(5) | not null | Wall-clock |
| VARCHAR(10) | default |
|
| INTEGER | default 0 | Calendar days after anchor day when |
| REAL | nullable | Multiplier on |
| REAL | nullable | Flat rate override; multiplied by |
customer_favorites
Composite primary key β no surrogate id column.
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, FK β users | |
| BIGINT | PK, FK β customers |
customer_access
Grants a non-admin user explicit visibility of a customer. Non-admin users with no row (direct or via group) cannot see the customer at all. Admins always see all customers regardless of this table.
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, FK β users | |
| BIGINT | PK, FK β customers | |
| VARCHAR | default |
|
Groups tables
user_groups
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| VARCHAR | not null | |
| TEXT | nullable |
group_members
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β user_groups | |
| BIGINT | FK β users |
group_project_access
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β user_groups | |
| BIGINT | FK β projects | |
| VARCHAR | default |
|
group_customer_access
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β user_groups | |
| BIGINT | FK β customers | |
| VARCHAR | default |
|
Scrum tables
sprints
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| BIGINT | FK β projects | |
| VARCHAR | not null | |
| TEXT | nullable | Sprint goal description |
| DATE | nullable | |
| DATE | nullable | |
| VARCHAR | default |
|
sprint_cards
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β sprints | |
| BIGINT | FK β cards |
Time tracking
time_entries
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| BIGINT | FK β users | |
| BIGINT | FK β customers, nullable | |
| BIGINT | FK β projects, nullable | |
| BIGINT | FK β cards, nullable | |
| BIGINT | FK β contracts, nullable | |
| DATE | not null | |
| INTEGER | not null | Duration in minutes |
| TEXT | nullable |
Discussion tables
topics
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| TIMESTAMP | nullable, index | |
| BIGINT | FK β projects | |
| BIGINT | FK β users | Author |
| VARCHAR | not null | |
| TEXT | not null | Markdown |
| BOOLEAN | default false | |
| BOOLEAN | default false | No new replies when locked |
topic_replies
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| TIMESTAMP | nullable, index | |
| BIGINT | FK β topics | |
| BIGINT | FK β users | |
| TEXT | not null | Markdown |
Chat tables
chat_messages
Project-level channel messages.
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| TIMESTAMP | nullable, index | |
| BIGINT | FK β projects | |
| BIGINT | FK β users | |
| TEXT | not null | Markdown |
| BOOLEAN | default false | Messages from webhook/CI integrations |
conversations
Direct messages and group DMs.
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| VARCHAR | nullable | Set for group conversations |
| BOOLEAN | default false |
conversation_members
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β conversations | |
| BIGINT | FK β users |
conversation_messages
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| TIMESTAMP | nullable, index | |
| BIGINT | FK β conversations | |
| BIGINT | FK β users | |
| TEXT | not null | Markdown |
Files
attachments
Polymorphic file table β one row per uploaded file, regardless of where it is attached.
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| VARCHAR | not null |
|
| BIGINT | not null | PK of the owning row |
| VARCHAR | not null | Stored filename (randomised hex) |
| VARCHAR | not null | Original filename as uploaded |
| VARCHAR | not null | Detected server-side from file content |
| BIGINT | not null | Bytes |
| BIGINT | FK β users |
Note | MIME type is detected from the first 512 bytes of the file using net/http.DetectContentType.
The Content-Type header sent by the client is ignored. |
Integration tables
card_links
Git commit and pull-request links attached to cards.
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| BIGINT | FK β cards | |
| VARCHAR | not null |
|
| VARCHAR | not null |
|
| VARCHAR | not null | |
| VARCHAR | nullable | PR/commit title |
| VARCHAR | nullable | PR state (e.g. |
project_webhooks
Inbound webhooks that push events from external systems into WarmDesk.
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| BIGINT | FK β projects | |
| VARCHAR | not null | Secret used to verify the request |
| VARCHAR | not null |
|
| BOOLEAN | default true |
api_keys
Long-lived API keys, primarily for CI/CD automation via the Ticket API.
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| BIGINT | FK β users | Owning user |
| BIGINT | FK β projects, nullable | Scope-limited to one project if set |
| VARCHAR | not null | Human label |
| VARCHAR | not null | SHA-256 of the raw key |
| VARCHAR | not null | First 8 chars, shown in the UI |
| TIMESTAMP | nullable | |
| TIMESTAMP | nullable | Null = no expiry |
System settings
system_settings
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| VARCHAR | unique, not null | Setting identifier |
| TEXT | not null | String value |
Settings are read at request time so changes take effect without restarting the server.
All valid keys are defined as constants in handlers/system.go.
Entity-relationship overview
users ββββββββββββββββββ project_members ββββ projects
β β
β columns
β β
βββ group_members ββ user_groups cards
β β β
β group_project_access βββββββββββ β
β group_customer_access ββββ customers
β β
βββ time_entries ββββββββββββββββββββββ contracts
β
βββ projects
βββ customers
βββ cardsKey relationships:
A project belongs to a customer (optional) and has many columns and cards.
project_members is the direct userβproject join table; group_project_access grants access to all group members at once.
cards belong to a column and a project; they can have a
parent_card_idfor sub-cards.attachments are polymorphic:
owner_type+owner_idpoint to any ofcard,card_comment,chat_message, orconv_message.time_entries link a user to any combination of customer, project, card, and contract.