Documentation
¶
Index ¶
- Constants
- type AcknowledgmentStatus
- type AcknowledgmentVia
- type ActionItem
- type ActionItemStatus
- type Alert
- type AlertSeverity
- type AlertStatus
- type EscalationPolicy
- type EscalationSeverityRule
- type EscalationState
- type EscalationStateStatus
- type EscalationTargetType
- type EscalationTier
- type GroupingRule
- type Incident
- type IncidentSeverity
- type IncidentStatus
- type JSONB
- type JSONBArray
- type LocalSession
- type PostMortem
- type PostMortemComment
- type PostMortemStatus
- type PostMortemTemplate
- type RotationType
- type RoutingRule
- type Schedule
- type ScheduleHoliday
- type ScheduleLayer
- type ScheduleOverride
- type ScheduleParticipant
- type ScheduleUnavailability
- type SlackConfig
- type SystemSetting
- type TeamsConfig
- type TelegramConfig
- type TimelineEntry
- type User
- type UserRole
Constants ¶
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 ¶
BeforeCreate is a GORM hook that runs before creating an alert It ensures ReceivedAt is set if not already specified (immutability)
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:
- Creates an EscalationState row for the alert.
- Notifies tier 0 targets immediately (on next worker poll).
- 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:
- Extract relevant label keys from MatchLabels (non-wildcard keys)
- Sort keys alphabetically for deterministic ordering
- Build string: "key1=value1|key2=value2|..."
- 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 ¶
BeforeCreate hook
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
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
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 ¶
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.
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 uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
ScheduleID uuid.UUID `gorm:"type:uuid;not null;index" json:"schedule_id"`
UserName string `gorm:"type:varchar(255);not null" json:"user_name"`
StartDate time.Time `gorm:"type:date;not null" json:"start_date"`
EndDate time.Time `gorm:"type:date;not null" json:"end_date"`
Reason string `gorm:"type:varchar(500)" json:"reason,omitempty"`
CreatedBy string `gorm:"type:varchar(255);not null;default:'system'" json:"created_by"`
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.