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 | ||
| TIMESTAMP | nullable, index | |
| VARCHAR | not null | |
| VARCHAR | unique, not null | |
| TEXT | nullable | |
| VARCHAR | nullable | |
| VARCHAR | nullable | Accent colour |
contracts
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| TIMESTAMP | ||
| TIMESTAMP | ||
| TIMESTAMP | nullable, index | |
| BIGINT | FK β customers | |
| VARCHAR | not null | |
| TEXT | nullable | |
| DATE | nullable | |
| DATE | nullable | |
| REAL | default 0 | Total contracted hours |
| VARCHAR | default |
|
customer_favorites
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β users | |
| BIGINT | FK β customers |
customer_access
Grants a user access to a specific customer (used when the user is not an admin and has no group access).
| Column | Type | Constraints | Notes |
|---|---|---|---|
| BIGINT | PK, auto | |
| BIGINT | FK β users | |
| BIGINT | 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.