models

package
v0.15.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 24, 2026 License: AGPL-3.0 Imports: 6 Imported by: 0

Documentation

Index

Constants

View Source
const (
	TimelineTypeIncidentCreated   = "incident_created"
	TimelineTypeStatusChanged     = "status_changed"
	TimelineTypeSeverityChanged   = "severity_changed"
	TimelineTypeAlertLinked       = "alert_linked"
	TimelineTypeMessage           = "message"
	TimelineTypeResponderAdded    = "responder_added"
	TimelineTypeEscalated         = "escalated"
	TimelineTypeSummaryGenerated  = "summary_generated"
	TimelineTypePostmortemCreated = "postmortem_created"
	TimelineTypeCommanderAssigned = "commander_assigned"
)

TimelineEntryType constants

Variables

This section is empty.

Functions

This section is empty.

Types

type AcknowledgmentStatus

type AcknowledgmentStatus string

AcknowledgmentStatus is the alert-level ack state, denormalized from escalation_states for fast querying without a join.

const (
	AcknowledgmentStatusPending      AcknowledgmentStatus = "pending"
	AcknowledgmentStatusAcknowledged AcknowledgmentStatus = "acknowledged"
	AcknowledgmentStatusTimedOut     AcknowledgmentStatus = "timed_out"
)

type AcknowledgmentVia

type AcknowledgmentVia string

AcknowledgmentVia identifies how an acknowledgment was received.

const (
	AcknowledgmentViaSlack AcknowledgmentVia = "slack"
	AcknowledgmentViaAPI   AcknowledgmentVia = "api"
	AcknowledgmentViaCLI   AcknowledgmentVia = "cli"
)

type ActionItem

type ActionItem struct {
	ID           uuid.UUID        `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	PostMortemID uuid.UUID        `gorm:"type:uuid;not null;index" json:"post_mortem_id"`
	Title        string           `gorm:"type:varchar(500);not null" json:"title"`
	Owner        *string          `gorm:"type:varchar(255)" json:"owner,omitempty"`
	DueDate      *time.Time       `json:"due_date,omitempty"`
	Status       ActionItemStatus `gorm:"type:varchar(20);not null;default:'open'" json:"status"`
	CreatedAt    time.Time        `gorm:"not null;default:now()" json:"created_at"`
	UpdatedAt    time.Time        `gorm:"not null;default:now()" json:"updated_at"`
}

ActionItem is a follow-up task extracted from a post-mortem. Cascade-deleted when its parent post-mortem is deleted.

func (*ActionItem) BeforeCreate

func (a *ActionItem) BeforeCreate(tx *gorm.DB) error

func (ActionItem) TableName

func (ActionItem) TableName() string

type ActionItemStatus

type ActionItemStatus string

ActionItemStatus represents the completion state of an action item.

const (
	ActionItemStatusOpen       ActionItemStatus = "open"
	ActionItemStatusInProgress ActionItemStatus = "in_progress"
	ActionItemStatusClosed     ActionItemStatus = "closed"
)

type Alert

type Alert struct {
	ID          uuid.UUID     `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	ExternalID  string        `gorm:"type:varchar(255);not null" json:"external_id"`
	Source      string        `gorm:"type:varchar(100);not null" json:"source"`
	Fingerprint string        `gorm:"type:varchar(255);index" json:"fingerprint"`
	Status      AlertStatus   `gorm:"type:varchar(50);not null;default:'firing'" json:"status"`
	Severity    AlertSeverity `gorm:"type:varchar(50);not null;default:'info'" json:"severity"`
	Title       string        `gorm:"type:varchar(500);not null" json:"title"`
	Description string        `gorm:"type:text" json:"description"`
	Labels      JSONB         `gorm:"type:jsonb;default:'{}'" json:"labels"`
	Annotations JSONB         `gorm:"type:jsonb;default:'{}'" json:"annotations"`
	RawPayload  JSONB         `gorm:"type:jsonb" json:"raw_payload"`
	StartedAt   time.Time     `gorm:"type:timestamptz;not null" json:"started_at"`
	EndedAt     *time.Time    `gorm:"type:timestamptz" json:"ended_at"`
	ReceivedAt  time.Time     `gorm:"type:timestamptz;not null;default:now()" json:"received_at"`
	CreatedAt   time.Time     `gorm:"type:timestamptz;not null;default:now()" json:"created_at"`

	// EscalationPolicyID links this alert to the escalation policy assigned by
	// the routing engine.  Nil means no escalation is configured for this alert.
	EscalationPolicyID *uuid.UUID `gorm:"type:uuid;index" json:"escalation_policy_id,omitempty"`

	// AcknowledgmentStatus is the alert-level ack state driven by the escalation
	// engine. Denormalized from escalation_states for fast querying without a join.
	AcknowledgmentStatus AcknowledgmentStatus `gorm:"type:varchar(50);not null;default:'pending'" json:"acknowledgment_status"`
}

Alert represents an alert from a monitoring system

func (*Alert) BeforeCreate

func (a *Alert) BeforeCreate(tx *gorm.DB) error

BeforeCreate is a GORM hook that runs before creating an alert It ensures ReceivedAt is set if not already specified (immutability)

func (Alert) TableName

func (Alert) TableName() string

TableName specifies the table name for the Alert model

type AlertSeverity

type AlertSeverity string

AlertSeverity represents the severity level of an alert

const (
	AlertSeverityCritical AlertSeverity = "critical"
	AlertSeverityWarning  AlertSeverity = "warning"
	AlertSeverityInfo     AlertSeverity = "info"
)

type AlertStatus

type AlertStatus string

AlertStatus represents the current state of an alert

const (
	AlertStatusFiring   AlertStatus = "firing"
	AlertStatusResolved AlertStatus = "resolved"
)

type EscalationPolicy

type EscalationPolicy struct {
	// ID is the unique identifier for this policy.
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// Name is a human-readable label.
	// Example: "Platform Team Default"
	Name string `gorm:"type:varchar(255);not null" json:"name"`

	// Description explains when and why this policy is used.
	Description string `gorm:"type:text;default:''" json:"description"`

	// Enabled controls whether this policy is evaluated by the engine.
	// Disabled policies are never triggered even if a routing rule references them.
	Enabled bool `gorm:"not null;default:true" json:"enabled"`

	// CreatedAt is when this policy was created (immutable, server-generated).
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`

	// UpdatedAt is when this policy was last modified.
	UpdatedAt time.Time `gorm:"not null;default:now()" json:"updated_at"`

	// Tiers are loaded via repository.GetPolicyWithTiers — not auto-loaded by GORM.
	Tiers []EscalationTier `gorm:"-" json:"tiers,omitempty"`
}

EscalationPolicy is a named, ordered chain of notification tiers.

When a critical alert fires and matches a routing rule that references this policy, the escalation engine:

  1. Creates an EscalationState row for the alert.
  2. Notifies tier 0 targets immediately (on next worker poll).
  3. Polls every 30 s; if the alert is unacknowledged for tier.TimeoutSeconds, advances to the next tier and notifies its targets.

Deleting a policy cascades to all its tiers.

func (*EscalationPolicy) BeforeCreate

func (p *EscalationPolicy) BeforeCreate(tx *gorm.DB) error

BeforeCreate sets the ID if not already set.

func (EscalationPolicy) TableName

func (EscalationPolicy) TableName() string

TableName specifies the database table name.

type EscalationSeverityRule

type EscalationSeverityRule struct {
	ID                 uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	Severity           string    `gorm:"type:varchar(50);not null;uniqueIndex"          json:"severity"`
	EscalationPolicyID uuid.UUID `gorm:"type:uuid;not null"                             json:"escalation_policy_id"`
	CreatedAt          time.Time `gorm:"not null;default:now()"                         json:"created_at"`
	UpdatedAt          time.Time `gorm:"not null;default:now()"                         json:"updated_at"`
}

EscalationSeverityRule maps an alert severity to a default escalation policy. At most one rule exists per severity (UNIQUE on severity).

func (EscalationSeverityRule) TableName

func (EscalationSeverityRule) TableName() string

TableName specifies the database table name.

type EscalationState

type EscalationState struct {
	// ID is the unique identifier for this state row.
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// AlertID is set for alert-sourced escalations (nullable for incident escalations).
	AlertID *uuid.UUID `gorm:"type:uuid;uniqueIndex" json:"alert_id,omitempty"`

	// IncidentID is set for incident-sourced (manual) escalations.
	IncidentID *uuid.UUID `gorm:"type:uuid;index" json:"incident_id,omitempty"`

	// SourceType distinguishes alert vs. incident escalations.
	SourceType string `gorm:"type:varchar(20);not null;default:'alert'" json:"source_type"`

	// PolicyID is the escalation policy driving this escalation.
	PolicyID uuid.UUID `gorm:"type:uuid;not null;index" json:"policy_id"`

	// CurrentTierIndex is the tier that was most recently notified.
	// Starts at 0; incremented by the worker on timeout.
	CurrentTierIndex int `gorm:"not null;default:0" json:"current_tier_index"`

	// Status reflects the current lifecycle stage of this escalation.
	Status EscalationStateStatus `gorm:"type:varchar(50);not null;default:'pending'" json:"status"`

	// LastNotifiedAt is when the current tier's notifications were last sent.
	// Nil means tier 0 has not yet been notified (status == pending).
	LastNotifiedAt *time.Time `gorm:"type:timestamptz" json:"last_notified_at,omitempty"`

	// AcknowledgedAt is when the escalation was stopped by a user.
	// Nil means not yet acknowledged.
	AcknowledgedAt *time.Time `gorm:"type:timestamptz" json:"acknowledged_at,omitempty"`

	// AcknowledgedBy is the user_name of whoever acknowledged the alert.
	AcknowledgedBy *string `gorm:"type:varchar(255)" json:"acknowledged_by,omitempty"`

	// AcknowledgedVia records the channel used: "slack", "api", or "cli".
	AcknowledgedVia *AcknowledgmentVia `gorm:"type:varchar(50)" json:"acknowledged_via,omitempty"`

	// CreatedAt is when this escalation was triggered (immutable, server-generated).
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`
}

EscalationState tracks the live escalation progress for a single alert or manually escalated incident.

Exactly one of AlertID / IncidentID is set, discriminated by SourceType. For alert-sourced states the legacy UNIQUE index on alert_id still applies. The worker queries for active states (acknowledged_at IS NULL) and advances the tier when last_notified_at + tier.timeout_seconds < NOW().

func (*EscalationState) BeforeCreate

func (s *EscalationState) BeforeCreate(tx *gorm.DB) error

BeforeCreate sets the ID if not already set.

func (*EscalationState) IsActive

func (s *EscalationState) IsActive() bool

IsActive returns true if this escalation is still in progress (not yet acknowledged or completed).

func (EscalationState) TableName

func (EscalationState) TableName() string

TableName specifies the database table name.

type EscalationStateStatus

type EscalationStateStatus string

EscalationStateStatus tracks the lifecycle of a single alert's escalation.

const (
	// EscalationStatePending means the escalation was triggered but no
	// notification has been sent yet (first worker poll hasn't run).
	EscalationStatePending EscalationStateStatus = "pending"

	// EscalationStateNotified means the worker has sent at least one DM for
	// the current tier and is waiting for acknowledgment or timeout.
	EscalationStateNotified EscalationStateStatus = "notified"

	// EscalationStateAcknowledged means a user has acknowledged the alert;
	// no further tier escalation will occur.
	EscalationStateAcknowledged EscalationStateStatus = "acknowledged"

	// EscalationStateCompleted means the escalation ended (alert resolved
	// before acknowledgment, or max tiers exhausted).
	EscalationStateCompleted EscalationStateStatus = "completed"
)

type EscalationTargetType

type EscalationTargetType string

EscalationTargetType defines how a tier resolves its notification targets.

const (
	// EscalationTargetSchedule notifies the currently on-call user from the
	// tier's schedule_id.
	EscalationTargetSchedule EscalationTargetType = "schedule"

	// EscalationTargetUsers notifies every user_name in the tier's user_names list.
	EscalationTargetUsers EscalationTargetType = "users"

	// EscalationTargetBoth notifies the schedule's on-call user AND all listed users.
	EscalationTargetBoth EscalationTargetType = "both"
)

type EscalationTier

type EscalationTier struct {
	// ID is the unique identifier for this tier.
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// PolicyID is the parent escalation policy.
	PolicyID uuid.UUID `gorm:"type:uuid;not null;index" json:"policy_id"`

	// TierIndex is the 0-based position in the escalation chain.
	// A UNIQUE constraint on (policy_id, tier_index) is enforced by the DB.
	TierIndex int `gorm:"not null;default:0" json:"tier_index"`

	// TimeoutSeconds is how long to wait at this tier before advancing.
	// Minimum 1 second; typical values are 300 (5 min) or 600 (10 min).
	TimeoutSeconds int `gorm:"not null;default:300" json:"timeout_seconds"`

	// TargetType defines which targets are notified.
	TargetType EscalationTargetType `gorm:"type:varchar(50);not null;default:'schedule'" json:"target_type"`

	// ScheduleID is the schedule whose on-call user will be notified.
	// Nullable: nil means no schedule target (only relevant when TargetType is
	// "schedule" or "both").  Set to nil if the referenced schedule is deleted.
	ScheduleID *uuid.UUID `gorm:"type:uuid;index" json:"schedule_id,omitempty"`

	// UserNames is a list of free-text user identifiers to notify directly.
	// Mirrors the user_name convention used in schedule_participants.
	// Example: ["alice", "@bob", "Carol Smith"]
	UserNames JSONBArray `gorm:"type:jsonb;not null;default:'[]'" json:"user_names"`

	// CreatedAt is when this tier was created (immutable, server-generated).
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`
}

EscalationTier is one step in an escalation policy.

Tiers are evaluated in ascending tier_index order. The engine notifies the tier's targets, then waits TimeoutSeconds before advancing to tier_index+1. If no higher tier exists, the escalation is marked completed.

target_type determines which targets are notified:

  • "schedule": on-call user resolved from ScheduleID at notification time
  • "users": every name in UserNames
  • "both": union of schedule on-call and UserNames

func (*EscalationTier) BeforeCreate

func (t *EscalationTier) BeforeCreate(tx *gorm.DB) error

BeforeCreate sets the ID if not already set.

func (EscalationTier) TableName

func (EscalationTier) TableName() string

TableName specifies the database table name.

type GroupingRule

type GroupingRule struct {
	// ID is the unique identifier for this grouping rule
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// Name is a human-readable identifier for this rule
	// Example: "Group by service and environment"
	Name string `gorm:"type:varchar(255);not null" json:"name"`

	// Description explains what this rule does and why it exists
	// Example: "Groups alerts from the same service in the same environment..."
	Description string `gorm:"type:text;default:''" json:"description"`

	// Enabled controls whether this rule is active
	// Disabled rules are skipped during alert processing
	Enabled bool `gorm:"not null" json:"enabled"`

	// Priority determines evaluation order (lower number = higher priority)
	// Rules are evaluated in ascending priority order until one matches
	// Example: Priority 1 is evaluated before Priority 100
	// UNIQUE constraint ensures no two rules have the same priority
	Priority int `gorm:"not null;uniqueIndex:idx_grouping_rules_priority" json:"priority"`

	// MatchLabels defines which alerts this rule applies to
	// Stored as JSONB map[string]string
	//
	// Matching logic:
	//   - Empty map {} matches all alerts
	//   - {"alertname": "*"} matches all alerts (wildcard)
	//   - {"alertname": "HighCPU"} matches alerts with exactly that alertname
	//   - {"service": "api", "env": "prod"} matches alerts with both labels
	//
	// Example: {"service": "*", "severity": "critical"} matches all critical alerts
	// that have a service label (regardless of value)
	MatchLabels JSONB `gorm:"type:jsonb;not null;default:'{}'" json:"match_labels"`

	// TimeWindowSeconds defines the grouping time window in seconds
	//
	// Alerts are grouped if:
	//   1. They match the same rule
	//   2. They have the same group key (derived from labels)
	//   3. An open incident exists within this time window
	//
	// Example: 300 (5 minutes) means alerts within 5 minutes are grouped together
	//
	// Use cases:
	//   - Short window (60s): Group rapid-fire alerts from same issue
	//   - Medium window (300s): Group related alerts during incident
	//   - Long window (3600s): Group slow-developing issues
	TimeWindowSeconds int `gorm:"not null;default:300" json:"time_window_seconds"`

	// CrossSourceLabels enables grouping alerts from different monitoring sources
	// Stored as JSONB []string
	//
	// When specified, alerts from different sources (prometheus, grafana, cloudwatch)
	// can be grouped together if they share the same values for these labels.
	//
	// Example: ["service", "env"] means:
	//   - Prometheus alert: {service: "api", env: "prod"}
	//   - Grafana alert: {service: "api", env: "prod"}
	//   → Both alerts grouped into same incident
	//
	// Use cases:
	//   - Cross-monitoring correlation: Same service failing in both Prometheus and Grafana
	//   - Multi-region deployments: Correlate alerts across CloudWatch regions
	//
	// Default: [] (empty array) means no cross-source grouping
	CrossSourceLabels JSONBArray `gorm:"type:jsonb;default:'[]'" json:"cross_source_labels"`

	// CreatedAt is when this rule was created (immutable)
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`

	// UpdatedAt is when this rule was last modified
	UpdatedAt time.Time `gorm:"not null;default:now()" json:"updated_at"`
}

GroupingRule defines how alerts should be grouped into a single incident.

Grouping rules are evaluated in priority order (lowest number = highest priority). The first matching rule determines the grouping behavior for an alert.

Example use cases:

  • "Group all alerts with same alertname within 5 minutes"
  • "Group alerts with same service label within 10 minutes"
  • "Group critical alerts from same region within 1 minute"

Grouping prevents alert fatigue by creating one incident for related alerts instead of separate incidents for each alert.

func (*GroupingRule) BeforeCreate

func (gr *GroupingRule) BeforeCreate(tx *gorm.DB) error

BeforeCreate generates a UUID for new grouping rules when none is set. This ensures compatibility with both PostgreSQL (which uses gen_random_uuid() as default) and SQLite (used in tests, which does not support that function).

func (*GroupingRule) GroupKey

func (gr *GroupingRule) GroupKey(alertLabels map[string]string) string

GroupKey generates a unique key for grouping alerts based on this rule.

The group key is derived from alert labels according to the rule's configuration. Alerts with the same group key (and within the time window) are grouped together.

Algorithm:

  1. Extract relevant label keys from MatchLabels (non-wildcard keys)
  2. Sort keys alphabetically for deterministic ordering
  3. Build string: "key1=value1|key2=value2|..."
  4. Hash with SHA256 for consistent, compact key

Example:

Rule: MatchLabels = {"service": "*", "env": "*"}
Alert: {service: "api", env: "prod", instance: "web-01"}
GroupKey: SHA256("env=prod|service=api") → "a1b2c3..."

Note: instance label is NOT included because it's not in MatchLabels. This ensures all alerts from the same service+env are grouped.

func (*GroupingRule) IsValid

func (gr *GroupingRule) IsValid() error

IsValid validates the grouping rule configuration.

Returns error if:

  • Name is empty
  • Priority is negative
  • TimeWindowSeconds is <= 0
  • MatchLabels is invalid JSON

This should be called before saving a rule to the database.

func (*GroupingRule) Matches

func (gr *GroupingRule) Matches(alertLabels map[string]string) bool

Matches checks if this rule applies to an alert based on its labels.

Returns true if all labels in MatchLabels match the alert's labels.

Matching logic:

  • Empty MatchLabels {} matches all alerts
  • Wildcard "*" matches any value for that key
  • Exact value must match exactly
  • If MatchLabels requires a key, alert must have that key

Examples:

Rule: {"alertname": "HighCPU"} matches alert with alertname=HighCPU
Rule: {"severity": "*"} matches any alert with a severity label
Rule: {} matches all alerts

func (GroupingRule) TableName

func (GroupingRule) TableName() string

TableName specifies the database table name

type Incident

type Incident struct {
	ID             uuid.UUID        `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	IncidentNumber int              `gorm:"autoIncrement;unique;not null" json:"incident_number"`
	Title          string           `gorm:"type:varchar(500);not null" json:"title"`
	Slug           string           `gorm:"type:varchar(100);not null" json:"slug"`
	Status         IncidentStatus   `gorm:"type:varchar(20);not null;default:'triggered';index:idx_incidents_status" json:"status"`
	Severity       IncidentSeverity `gorm:"type:varchar(20);not null;default:'medium';index:idx_incidents_severity" json:"severity"`
	Summary        string           `gorm:"type:text" json:"summary,omitempty"`

	// Slack integration
	SlackChannelID   string `gorm:"type:varchar(50)" json:"slack_channel_id,omitempty"`
	SlackChannelName string `gorm:"type:varchar(100)" json:"slack_channel_name,omitempty"`
	SlackMessageTS   string `gorm:"type:varchar(64)" json:"slack_message_ts,omitempty"`

	// Teams integration (v0.8+)
	// TeamsChannelID is the channel ID in the format "19:xxx@thread.tacv2"
	// TeamsConversationID is the Bot Framework conversation ID (a:xxx) — distinct from channel ID,
	// required for proactive messaging via Bot Framework REST API (v0.9+)
	// TeamsActivityID is the ID of the root adaptive card posted in the channel (used for updates)
	TeamsChannelID      *string `gorm:"type:varchar(255)" json:"teams_channel_id,omitempty"`
	TeamsChannelName    *string `gorm:"type:varchar(255)" json:"teams_channel_name,omitempty"`
	TeamsConversationID *string `gorm:"type:varchar(500)" json:"teams_conversation_id,omitempty"`
	TeamsActivityID     *string `gorm:"type:varchar(1024)" json:"teams_activity_id,omitempty"`

	// Timestamps (created_at and triggered_at are immutable)
	CreatedAt      time.Time  `gorm:"not null;default:now()" json:"created_at"`
	TriggeredAt    time.Time  `gorm:"not null;default:now();index:idx_incidents_triggered_at" json:"triggered_at"`
	AcknowledgedAt *time.Time `json:"acknowledged_at,omitempty"`
	ResolvedAt     *time.Time `json:"resolved_at,omitempty"`

	// Ownership
	CreatedByType string     `gorm:"type:varchar(20);not null" json:"created_by_type"`
	CreatedByID   string     `gorm:"type:varchar(100)" json:"created_by_id,omitempty"`
	CommanderID   *uuid.UUID `gorm:"type:uuid" json:"commander_id,omitempty"`
	CommanderName string     `gorm:"-" json:"-"` // resolved at service layer, never stored

	// Metadata
	Labels       JSONB `gorm:"type:jsonb;default:'{}'" json:"labels"`
	CustomFields JSONB `gorm:"type:jsonb;default:'{}'" json:"custom_fields"`

	// Grouping (v0.3+)
	// GroupKey is a hash derived from alert labels according to grouping rules
	// Alerts with the same group_key (within time window) are grouped into this incident
	// NULL for manually created incidents or incidents created before v0.3
	GroupKey *string `gorm:"type:varchar(64);index:idx_incidents_group_key_status_created" json:"group_key,omitempty"`

	// AIEnabled controls whether AI agents process this incident.
	// Default true. Can be set false via routing rules, integration defaults, or the Properties panel.
	AIEnabled bool `gorm:"not null;default:true;column:ai_enabled" json:"ai_enabled"`

	// AI Summarization (v0.6+)
	// AISummary is an on-demand AI-generated summary, distinct from the manual Summary field.
	// AISummaryGeneratedAt records when the summary was last regenerated.
	AISummary            *string    `gorm:"type:text" json:"ai_summary,omitempty"`
	AISummaryGeneratedAt *time.Time `gorm:"type:timestamptz" json:"ai_summary_generated_at,omitempty"`

	// Relationships (not in database, loaded via joins)
	Alerts []Alert `gorm:"many2many:incident_alerts;" json:"alerts,omitempty"`
}

Incident represents an incident

func (*Incident) BeforeCreate

func (i *Incident) BeforeCreate(tx *gorm.DB) error

BeforeCreate hook

func (Incident) TableName

func (Incident) TableName() string

TableName specifies the table name for Incident

type IncidentSeverity

type IncidentSeverity string

IncidentSeverity represents the severity of an incident

const (
	IncidentSeverityCritical IncidentSeverity = "critical"
	IncidentSeverityHigh     IncidentSeverity = "high"
	IncidentSeverityMedium   IncidentSeverity = "medium"
	IncidentSeverityLow      IncidentSeverity = "low"
)

type IncidentStatus

type IncidentStatus string

IncidentStatus represents the status of an incident

const (
	IncidentStatusTriggered    IncidentStatus = "triggered"
	IncidentStatusAcknowledged IncidentStatus = "acknowledged"
	IncidentStatusResolved     IncidentStatus = "resolved"
	IncidentStatusCanceled     IncidentStatus = "canceled"
)

type JSONB

type JSONB map[string]interface{}

JSONB is a custom type for PostgreSQL JSONB fields

func (*JSONB) Scan

func (j *JSONB) Scan(value interface{}) error

Scan implements the sql.Scanner interface for JSONB

func (JSONB) Value

func (j JSONB) Value() (driver.Value, error)

Value implements the driver.Valuer interface for JSONB

type JSONBArray

type JSONBArray []string

JSONBArray is a custom type for PostgreSQL JSONB array fields

func (*JSONBArray) Scan

func (j *JSONBArray) Scan(value interface{}) error

Scan implements the sql.Scanner interface for JSONBArray

func (JSONBArray) Value

func (j JSONBArray) Value() (driver.Value, error)

Value implements the driver.Valuer interface for JSONBArray

type LocalSession

type LocalSession struct {
	Token     string    `gorm:"type:text;primaryKey"           json:"-"`
	UserID    uuid.UUID `gorm:"type:uuid;not null;index"        json:"-"`
	ExpiresAt time.Time `gorm:"not null"                        json:"-"`
	CreatedAt time.Time `gorm:"not null;default:now()"          json:"-"`
}

LocalSession stores a session token for locally-authenticated users.

func (LocalSession) TableName

func (LocalSession) TableName() string

type PostMortem

type PostMortem struct {
	ID           uuid.UUID        `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	IncidentID   uuid.UUID        `gorm:"type:uuid;not null;uniqueIndex:idx_post_mortems_incident_id" json:"incident_id"`
	TemplateID   *uuid.UUID       `gorm:"type:uuid" json:"template_id,omitempty"`
	TemplateName string           `gorm:"type:varchar(100);not null;default:'Standard'" json:"template_name"`
	Status       PostMortemStatus `gorm:"type:varchar(20);not null;default:'draft'" json:"status"`
	Content      string           `gorm:"type:text;not null;default:''" json:"content"`
	GeneratedBy  string           `gorm:"type:varchar(20);not null;default:'ai'" json:"generated_by"`
	GeneratedAt  *time.Time       `json:"generated_at,omitempty"`
	PublishedAt  *time.Time       `json:"published_at,omitempty"`
	CreatedByID  string           `gorm:"type:varchar(255);not null;default:'system'" json:"created_by_id"`
	CreatedAt    time.Time        `gorm:"not null;default:now()" json:"created_at"`
	UpdatedAt    time.Time        `gorm:"not null;default:now()" json:"updated_at"`

	// Relationships (loaded explicitly, not by GORM auto-join)
	ActionItems []ActionItem `gorm:"-" json:"action_items,omitempty"`
}

PostMortem is the AI-generated (or manually authored) post-mortem document for an incident. One per incident, enforced by a unique index. Content is stored as Markdown and is fully editable after generation.

func (*PostMortem) BeforeCreate

func (p *PostMortem) BeforeCreate(tx *gorm.DB) error

func (PostMortem) TableName

func (PostMortem) TableName() string

type PostMortemComment

type PostMortemComment struct {
	ID           uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	PostMortemID uuid.UUID `gorm:"type:uuid;not null;index"                       json:"post_mortem_id"`
	AuthorID     string    `gorm:"type:varchar(255);not null;default:'anonymous'" json:"author_id"`
	AuthorName   string    `gorm:"type:varchar(255);not null;default:'Unknown'"   json:"author_name"`
	Content      string    `gorm:"type:text;not null"                             json:"content"`
	CreatedAt    time.Time `gorm:"not null;default:now()"                         json:"created_at"`
}

PostMortemComment is a discussion note left on a post-mortem by any team member. Comments are immutable once created — no edit endpoint. Delete only.

func (*PostMortemComment) BeforeCreate

func (c *PostMortemComment) BeforeCreate(tx *gorm.DB) error

func (PostMortemComment) TableName

func (PostMortemComment) TableName() string

type PostMortemStatus

type PostMortemStatus string

PostMortemStatus represents the lifecycle state of a post-mortem document.

const (
	PostMortemStatusDraft     PostMortemStatus = "draft"
	PostMortemStatusPublished PostMortemStatus = "published"
)

type PostMortemTemplate

type PostMortemTemplate struct {
	ID          uuid.UUID  `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	Name        string     `gorm:"type:varchar(100);not null;uniqueIndex" json:"name"`
	Description string     `gorm:"type:text;not null;default:''" json:"description"`
	Sections    JSONBArray `gorm:"type:jsonb;not null;default:'[]'" json:"sections"`
	IsBuiltIn   bool       `gorm:"not null;default:false" json:"is_built_in"`
	CreatedAt   time.Time  `gorm:"not null;default:now()" json:"created_at"`
	UpdatedAt   time.Time  `gorm:"not null;default:now()" json:"updated_at"`
}

PostMortemTemplate is a reusable template that defines the sections included in an AI-generated post-mortem. Built-in templates are seeded at migration time; users may create, edit, or delete any template.

func (*PostMortemTemplate) BeforeCreate

func (t *PostMortemTemplate) BeforeCreate(tx *gorm.DB) error

func (PostMortemTemplate) TableName

func (PostMortemTemplate) TableName() string

type RotationType

type RotationType string

RotationType defines the cadence of a schedule layer's rotation.

const (
	// RotationTypeDaily rotates participants every day.
	RotationTypeDaily RotationType = "daily"
	// RotationTypeWeekly rotates participants every week.
	RotationTypeWeekly RotationType = "weekly"
	// RotationTypeCustom rotates based on shift_duration_seconds.
	RotationTypeCustom RotationType = "custom"
)

type RoutingRule

type RoutingRule struct {
	// ID is the unique identifier for this routing rule
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// Name is a human-readable identifier for this rule
	Name string `gorm:"type:varchar(255);not null" json:"name"`

	// Description explains what this rule does and why it exists
	Description string `gorm:"type:text;default:''" json:"description"`

	// Enabled controls whether this rule is active
	Enabled bool `gorm:"not null;default:true" json:"enabled"`

	// Priority determines evaluation order (lower number = higher priority).
	// UNIQUE constraint ensures no two rules share the same priority.
	Priority int `gorm:"not null;uniqueIndex:idx_routing_rules_priority" json:"priority"`

	// MatchCriteria defines which alerts this rule applies to (JSONB).
	// Empty map {} matches all alerts.
	MatchCriteria JSONB `gorm:"type:jsonb;not null;default:'{}'" json:"match_criteria"`

	// Actions defines what to do when this rule matches (JSONB).
	// Supported keys: create_incident, suppress, severity_override, channel_override.
	Actions JSONB `gorm:"type:jsonb;not null;default:'{}'" json:"actions"`

	// CreatedAt is when this rule was created (immutable)
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`

	// UpdatedAt is when this rule was last modified
	UpdatedAt time.Time `gorm:"not null;default:now()" json:"updated_at"`
}

RoutingRule defines how alerts should be routed after grouping.

Routing rules are evaluated in priority order (lowest number = highest priority). The first matching rule determines what happens to the alert.

match_criteria JSONB schema:

{
  "source":   ["prometheus", "grafana"],   // optional; empty = all sources
  "severity": ["critical", "warning"],     // optional; empty = all severities
  "labels":   {"env": "prod", "svc": "*"} // optional; * matches any value
}

actions JSONB schema:

{
  "create_incident":  true,       // default action
  "suppress":         true,       // alert stored, no incident created
  "severity_override": "critical", // override alert severity
  "channel_override":  "db-oncall" // override Slack channel name suffix
}

func (RoutingRule) TableName

func (RoutingRule) TableName() string

TableName specifies the database table name

type Schedule

type Schedule struct {
	// ID is the unique identifier for this schedule.
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// Name is a human-readable label.
	// Example: "Platform Team Primary"
	Name string `gorm:"type:varchar(255);not null" json:"name"`

	// Description explains the purpose of this schedule.
	Description string `gorm:"type:text;default:''" json:"description"`

	// Timezone is an IANA timezone string used for shift boundary calculations.
	// Example: "America/New_York", "UTC"
	Timezone string `gorm:"type:varchar(100);not null;default:'UTC'" json:"timezone"`

	// NotificationChannel is an optional Slack channel or other destination
	// to post shift-change notifications to. Free-form string; may be empty.
	// Example: "#oncall-platform"
	NotificationChannel string `gorm:"type:varchar(255);default:''" json:"notification_channel"`

	// DefaultEscalationPolicyID optionally links this schedule to an escalation
	// policy that fires when alerts are routed to this schedule but no explicit
	// policy is set on the routing rule.
	DefaultEscalationPolicyID *uuid.UUID `gorm:"type:uuid" json:"default_escalation_policy_id,omitempty"`

	// CreatedAt is when this schedule was created (immutable, server-generated).
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`

	// UpdatedAt is when this schedule was last modified.
	UpdatedAt time.Time `gorm:"not null;default:now()" json:"updated_at"`

	// Layers are loaded via GetWithLayers — not auto-loaded by GORM.
	// Populated only when explicitly fetched.
	Layers []ScheduleLayer `gorm:"-" json:"layers,omitempty"`

	// HolidayCountries is the list of ISO 3166-1 country codes for which public
	// holidays are surfaced on this schedule. Loaded separately from
	// schedule_holiday_configs; not a real column on the schedules table.
	HolidayCountries []string `gorm:"-" json:"holiday_countries"`
}

Schedule is the top-level on-call schedule entity.

A schedule contains one or more layers. During evaluation the layer with the lowest order_index that has a non-empty participant slot for the queried time wins (fallback chain). Overrides are checked before layers.

func (*Schedule) BeforeCreate

func (s *Schedule) BeforeCreate(_ *gorm.DB) error

BeforeCreate generates a UUID if none is set. Needed for SQLite tests where gen_random_uuid() is unavailable; PostgreSQL production uses the GORM-set value.

func (Schedule) TableName

func (Schedule) TableName() string

TableName specifies the database table name.

type ScheduleHoliday

type ScheduleHoliday struct {
	ID          uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	ScheduleID  uuid.UUID `gorm:"type:uuid;not null;index"                      json:"schedule_id"`
	CountryCode string    `gorm:"type:varchar(10);not null"                     json:"country_code"`
	Date        time.Time `gorm:"type:date;not null"                            json:"date"`
	Name        string    `gorm:"type:varchar(255);not null"                    json:"name"`
	CreatedAt   time.Time `gorm:"not null;default:now()"                        json:"created_at"`
}

ScheduleHoliday is a single public holiday date for a schedule, fetched from a country's ICS feed and stored locally for offline access.

func (ScheduleHoliday) TableName

func (ScheduleHoliday) TableName() string

TableName specifies the database table name.

type ScheduleLayer

type ScheduleLayer struct {
	// ID is the unique identifier for this layer.
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// ScheduleID is the parent schedule.
	ScheduleID uuid.UUID `gorm:"type:uuid;not null;index" json:"schedule_id"`

	// Name is a human-readable label.
	// Example: "Primary", "Secondary"
	Name string `gorm:"type:varchar(255);not null" json:"name"`

	// OrderIndex determines evaluation precedence. Lower wins.
	// 0 = primary, 1 = secondary, etc.
	OrderIndex int `gorm:"not null;default:0" json:"order_index"`

	// RotationType defines the cadence: "daily", "weekly", or "custom".
	RotationType RotationType `gorm:"type:varchar(50);not null;default:'weekly'" json:"rotation_type"`

	// RotationStart is the epoch from which shift slots are computed.
	// Defaults to midnight UTC on the day the layer is created.
	// All slot boundaries are: RotationStart + N * ShiftDurationSeconds.
	RotationStart time.Time `gorm:"not null" json:"rotation_start"`

	// ShiftDurationSeconds is the length of one shift in seconds.
	// For "daily" this is 86400, for "weekly" 604800.
	// For "custom" the caller sets it explicitly.
	ShiftDurationSeconds int `gorm:"not null;default:604800" json:"shift_duration_seconds"`

	// CreatedAt is when this layer was created (immutable, server-generated).
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`

	// Participants are loaded alongside the layer — not auto-loaded by GORM.
	Participants []ScheduleParticipant `gorm:"-" json:"participants,omitempty"`
}

ScheduleLayer defines one rotation layer within a schedule.

Layers are stacked by order_index. The evaluator walks layers 0, 1, 2, … and the first layer that yields a non-empty user for the requested time wins. This models primary/secondary/tertiary on-call without special-casing.

func (*ScheduleLayer) BeforeCreate

func (l *ScheduleLayer) BeforeCreate(_ *gorm.DB) error

BeforeCreate generates a UUID if none is set.

func (ScheduleLayer) TableName

func (ScheduleLayer) TableName() string

TableName specifies the database table name.

type ScheduleOverride

type ScheduleOverride struct {
	// ID is the unique identifier for this override.
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// ScheduleID is the parent schedule.
	ScheduleID uuid.UUID `gorm:"type:uuid;not null;index" json:"schedule_id"`

	// OverrideUser is the user taking over on-call during this window.
	OverrideUser string `gorm:"type:varchar(255);not null" json:"override_user"`

	// StartTime is the beginning of the override window (inclusive).
	StartTime time.Time `gorm:"not null" json:"start_time"`

	// EndTime is the end of the override window (exclusive).
	EndTime time.Time `gorm:"not null" json:"end_time"`

	// CreatedBy is the user_name of whoever created this override.
	CreatedBy string `gorm:"type:varchar(255);not null;default:'system'" json:"created_by"`

	// CreatedAt is when this override was created (immutable, server-generated).
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`
}

ScheduleOverride temporarily replaces the computed on-call user for a specific time range within a schedule.

Overrides are checked before layers: if any override covers the queried time, its user is returned without consulting layers.

func (ScheduleOverride) TableName

func (ScheduleOverride) TableName() string

TableName specifies the database table name.

type ScheduleParticipant

type ScheduleParticipant struct {
	// ID is the unique identifier for this participant slot.
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// LayerID is the parent layer.
	LayerID uuid.UUID `gorm:"type:uuid;not null;index" json:"layer_id"`

	// UserName is the display name or identifier for the on-call person.
	// Free-text; not a foreign key. Examples: "alice", "@alice", "Alice Smith".
	UserName string `gorm:"type:varchar(255);not null" json:"user_name"`

	// OrderIndex determines the rotation order within the layer.
	// Slot 0 is on-call first from RotationStart, then slot 1, etc.
	OrderIndex int `gorm:"not null;default:0" json:"order_index"`

	// CreatedAt is when this participant was added (immutable, server-generated).
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`
}

ScheduleParticipant is a single user slot within a layer.

Participants are ordered by order_index to define rotation order. The on-call user at time T is: participants[slotIndex % len(participants)] where slotIndex = floor((T - RotationStart) / ShiftDuration).

func (*ScheduleParticipant) BeforeCreate

func (p *ScheduleParticipant) BeforeCreate(_ *gorm.DB) error

BeforeCreate generates a UUID if none is set.

func (ScheduleParticipant) TableName

func (ScheduleParticipant) TableName() string

TableName specifies the database table name.

type ScheduleUnavailability

type ScheduleUnavailability struct {
	// ID is the unique identifier for this unavailability record.
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`

	// ScheduleID is the parent schedule.
	ScheduleID uuid.UUID `gorm:"type:uuid;not null;index" json:"schedule_id"`

	// UserName is the user who is unavailable.
	UserName string `gorm:"type:varchar(255);not null" json:"user_name"`

	// StartDate is the first day of unavailability (inclusive, stored as DATE).
	StartDate time.Time `gorm:"type:date;not null" json:"start_date"`

	// EndDate is the last day of unavailability (inclusive, stored as DATE).
	EndDate time.Time `gorm:"type:date;not null" json:"end_date"`

	// Reason is an optional human-readable explanation (e.g., "PTO", "sick leave").
	Reason string `gorm:"type:varchar(500)" json:"reason,omitempty"`

	// CreatedBy is the user_name of whoever created this record.
	CreatedBy string `gorm:"type:varchar(255);not null;default:'system'" json:"created_by"`

	// CreatedAt is when this record was created (immutable, server-generated).
	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`
}

ScheduleUnavailability marks a user as unavailable for on-call during a date range. Unlike overrides (which appoint a replacement), an unavailability causes the rotation to automatically advance to the next eligible participant.

func (ScheduleUnavailability) TableName

func (ScheduleUnavailability) TableName() string

TableName specifies the database table name.

type SlackConfig

type SlackConfig struct {
	ID                int        `gorm:"primaryKey;default:1"`
	BotToken          string     `gorm:"column:bot_token"`
	SigningSecret     string     `gorm:"column:signing_secret"`
	AppToken          string     `gorm:"column:app_token"`
	WorkspaceID       string     `gorm:"column:workspace_id"`
	WorkspaceName     string     `gorm:"column:workspace_name"`
	BotUserID         string     `gorm:"column:bot_user_id"`
	OAuthClientID     string     `gorm:"column:oauth_client_id"`
	OAuthClientSecret string     `gorm:"column:oauth_client_secret"`
	ConnectedAt       time.Time  `gorm:"column:connected_at;autoCreateTime"`
	ConnectedBy       *uuid.UUID `gorm:"column:connected_by"`
}

SlackConfig holds the Slack integration configuration for the entire instance. Only one row exists (enforced by CHECK id = 1).

func (SlackConfig) TableName

func (SlackConfig) TableName() string

type SystemSetting

type SystemSetting struct {
	Key       string    `gorm:"primaryKey"      json:"key"`
	Value     string    `gorm:"type:jsonb"      json:"value"`
	UpdatedAt time.Time `gorm:"not null;autoUpdateTime" json:"updated_at"`
}

SystemSetting is a generic key-value row for system-wide configuration. The value column stores arbitrary JSON (string, number, object, or null).

func (SystemSetting) TableName

func (SystemSetting) TableName() string

TableName specifies the database table name.

type TeamsConfig

type TeamsConfig struct {
	ID          int        `gorm:"primaryKey;default:1"`
	AppID       string     `gorm:"column:app_id"`
	AppPassword string     `gorm:"column:app_password"`
	TenantID    string     `gorm:"column:tenant_id"`
	TeamID      string     `gorm:"column:team_id"`
	BotUserID   string     `gorm:"column:bot_user_id"`
	ServiceURL  string     `gorm:"column:service_url"`
	TeamName    string     `gorm:"column:team_name"`
	ConnectedAt time.Time  `gorm:"column:connected_at;autoCreateTime"`
	ConnectedBy *uuid.UUID `gorm:"column:connected_by"`
}

TeamsConfig holds the Microsoft Teams integration configuration for the entire instance. Only one row exists (enforced by CHECK id = 1).

func (TeamsConfig) TableName

func (TeamsConfig) TableName() string

type TelegramConfig

type TelegramConfig struct {
	ID          int        `gorm:"primaryKey;default:1"`
	BotToken    string     `gorm:"column:bot_token"`
	ChatID      string     `gorm:"column:chat_id"`
	ChatName    string     `gorm:"column:chat_name"`
	ConnectedAt time.Time  `gorm:"column:connected_at;autoCreateTime"`
	ConnectedBy *uuid.UUID `gorm:"column:connected_by"`
}

TelegramConfig holds the Telegram bot integration configuration. Only one row exists (enforced by CHECK id = 1).

func (TelegramConfig) TableName

func (TelegramConfig) TableName() string

type TimelineEntry

type TimelineEntry struct {
	ID         uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	IncidentID uuid.UUID `gorm:"type:uuid;not null;index:idx_timeline_incident_timestamp" json:"incident_id"`
	Timestamp  time.Time `gorm:"not null;default:now();index:idx_timeline_incident_timestamp" json:"timestamp"`
	Type       string    `gorm:"type:varchar(50);not null" json:"type"`
	ActorType  string    `gorm:"type:varchar(20);not null" json:"actor_type"`
	ActorID    string    `gorm:"type:varchar(100)" json:"actor_id,omitempty"`
	Content    JSONB     `gorm:"type:jsonb;not null" json:"content"`
	CreatedAt  time.Time `gorm:"not null;default:now()" json:"created_at"`

	// Relationship
	Incident *Incident `gorm:"foreignKey:IncidentID" json:"incident,omitempty"`
}

TimelineEntry represents an immutable timeline entry for an incident

func (*TimelineEntry) BeforeCreate

func (t *TimelineEntry) BeforeCreate(tx *gorm.DB) error

BeforeCreate hook

func (TimelineEntry) TableName

func (TimelineEntry) TableName() string

TableName specifies the table name for TimelineEntry

type User

type User struct {
	ID    uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	Email string    `gorm:"type:varchar(255);not null;uniqueIndex"          json:"email"`
	Name  string    `gorm:"type:varchar(255);not null;default:''"           json:"name"`

	// SAMLSubject is the NameID from the SAML assertion — immutable after first login.
	// Nullable so that locally-authenticated users can be stored without a SAML subject.
	SAMLSubject   *string `gorm:"type:varchar(500);uniqueIndex;column:saml_subject" json:"-"`
	SAMLIDPIssuer string  `gorm:"type:varchar(500);not null;default:'';column:saml_idp_issuer" json:"-"`

	// Local auth fields — only set for auth_source='local'.
	PasswordHash *string `gorm:"type:text;column:password_hash" json:"-"`
	AuthSource   string  `gorm:"type:varchar(20);not null;default:'saml';column:auth_source" json:"-"`

	// AgentType identifies AI agent accounts. NULL for all human users.
	// Valid values: "postmortem", "triage", "comms", "oncall", "commander"
	AgentType *string `gorm:"type:varchar(50);column:agent_type" json:"agent_type,omitempty"`

	// Active controls whether this user (or agent) can operate.
	// Defaults to true for all existing rows via migration.
	Active bool `gorm:"not null;default:true;column:active" json:"active"`

	// SlackUserID is the Slack member ID (e.g. U0AJLLY3678).
	// Set automatically when user is imported from Slack, or manually by admin.
	// Used to invite on-call responders to incident channels and send DMs.
	SlackUserID *string `gorm:"type:varchar(20);column:slack_user_id" json:"slack_user_id,omitempty"`

	// TeamsUserID is the Azure AD Object ID (e.g. 29dcb621-b60b-4b3d-aa41-...).
	// Set automatically when user is imported from Teams, or manually by admin.
	// Used to send proactive Adaptive Card DMs via Bot Framework.
	TeamsUserID *string `gorm:"type:varchar(255);column:teams_user_id" json:"teams_user_id,omitempty"`

	Role        UserRole   `gorm:"type:varchar(50);not null;default:'member'" json:"role"`
	LastLoginAt *time.Time `json:"last_login_at,omitempty"`

	CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at"`
	UpdatedAt time.Time `gorm:"not null;default:now()" json:"updated_at"`
}

User represents a person or AI agent in Fluidify Regen. Human users authenticate via SAML SSO (auth_source='saml') or local credentials (auth_source='local'). AI agent users have auth_source='ai', a non-null AgentType, and no password. SAML users have no password hash; local users have no SAML subject. Human users are provisioned automatically on first login (JIT provisioning for SAML) or by an admin (for local auth). AI agent users are seeded on startup.

func (*User) BeforeCreate

func (u *User) BeforeCreate(tx *gorm.DB) error

func (User) TableName

func (User) TableName() string

type UserRole

type UserRole string

UserRole defines the access level of a user within Fluidify Regen.

const (
	UserRoleAdmin  UserRole = "admin"
	UserRoleMember UserRole = "member"
	UserRoleViewer UserRole = "viewer"
)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL