schema

package
v0.0.14 Latest Latest
Warning

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

Go to latest
Published: Jan 4, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Index

Constants

View Source
const (
	EnvironmentTypeDevelopment = "development"
	EnvironmentTypeProduction  = "production"
	EnvironmentTypeStaging     = "staging"
	EnvironmentTypePreview     = "preview"
	EnvironmentTypeTest        = "test"
)

Environment types

View Source
const (
	EnvironmentStatusActive   = "active"
	EnvironmentStatusInactive = "inactive"
)

Environment status

View Source
const (
	PromotionStatusPending    = "pending"
	PromotionStatusInProgress = "in_progress"
	PromotionStatusCompleted  = "completed"
	PromotionStatusFailed     = "failed"
)

Promotion status

View Source
const (
	OrgMemberRoleOwner  = "owner"
	OrgMemberRoleAdmin  = "admin"
	OrgMemberRoleMember = "member"
)

Organization Member Roles

View Source
const (
	OrgMemberStatusActive    = "active"
	OrgMemberStatusSuspended = "suspended"
	OrgMemberStatusPending   = "pending"
)

Organization Member Statuses

View Source
const (
	OrgInvitationStatusPending   = "pending"
	OrgInvitationStatusAccepted  = "accepted"
	OrgInvitationStatusExpired   = "expired"
	OrgInvitationStatusCancelled = "cancelled"
	OrgInvitationStatusDeclined  = "declined"
)

Organization Invitation Statuses

Variables

View Source
var ProviderDefaultScopes = map[string][]string{
	"google":    {"openid", "email", "profile"},
	"github":    {"user:email", "read:user"},
	"microsoft": {"openid", "email", "profile", "User.Read"},
	"apple":     {"name", "email"},
	"facebook":  {"email", "public_profile"},
	"discord":   {"identify", "email"},
	"twitter":   {"users.read", "tweet.read"},
	"linkedin":  {"openid", "profile", "email"},
	"spotify":   {"user-read-email", "user-read-private"},
	"twitch":    {"user:read:email"},
	"dropbox":   {"account_info.read"},
	"gitlab":    {"read_user", "openid", "email"},
	"line":      {"profile", "openid", "email"},
	"reddit":    {"identity"},
	"slack":     {"users:read", "users:read.email"},
	"bitbucket": {"account", "email"},
	"notion":    {},
}

ProviderDefaultScopes maps provider names to their default OAuth scopes

View Source
var ProviderDisplayNames = map[string]string{
	"google":    "Google",
	"github":    "GitHub",
	"microsoft": "Microsoft",
	"apple":     "Apple",
	"facebook":  "Facebook",
	"discord":   "Discord",
	"twitter":   "Twitter / X",
	"linkedin":  "LinkedIn",
	"spotify":   "Spotify",
	"twitch":    "Twitch",
	"dropbox":   "Dropbox",
	"gitlab":    "GitLab",
	"line":      "LINE",
	"reddit":    "Reddit",
	"slack":     "Slack",
	"bitbucket": "Bitbucket",
	"notion":    "Notion",
}

ProviderDisplayNames maps provider names to human-readable display names

View Source
var SupportedProviders = []string{
	"google",
	"github",
	"microsoft",
	"apple",
	"facebook",
	"discord",
	"twitter",
	"linkedin",
	"spotify",
	"twitch",
	"dropbox",
	"gitlab",
	"line",
	"reddit",
	"slack",
	"bitbucket",
	"notion",
}

SupportedProviders returns the list of all supported OAuth provider names

Functions

func GetProviderDefaultScopes added in v0.0.3

func GetProviderDefaultScopes(name string) []string

GetProviderDefaultScopes returns the default scopes for a provider

func GetProviderDisplayName added in v0.0.3

func GetProviderDisplayName(name string) string

GetProviderDisplayName returns the display name for a provider

func IsValidOrgInvitationStatus

func IsValidOrgInvitationStatus(status string) bool

IsValidOrgInvitationStatus checks if an invitation status is valid

func IsValidOrgMemberRole

func IsValidOrgMemberRole(role string) bool

IsValidOrgMemberRole checks if a role is valid

func IsValidOrgMemberStatus

func IsValidOrgMemberStatus(status string) bool

IsValidOrgMemberStatus checks if a status is valid

func IsValidProvider added in v0.0.3

func IsValidProvider(name string) bool

IsValidProvider checks if the given provider name is supported

func ValidOrgInvitationStatuses

func ValidOrgInvitationStatuses() []string

ValidOrgInvitationStatuses returns the list of valid invitation statuses

func ValidOrgMemberRoles

func ValidOrgMemberRoles() []string

ValidOrgMemberRoles returns the list of valid member roles

func ValidOrgMemberStatuses

func ValidOrgMemberStatuses() []string

ValidOrgMemberStatuses returns the list of valid member statuses

Types

type APIKey

type APIKey struct {
	AuditableModel
	bun.BaseModel `bun:"table:api_keys"`

	// 3-tier context (V2 architecture)
	AppID          xid.ID  `bun:"app_id,notnull,type:varchar(20)" json:"appID"`                     // Platform tenant
	EnvironmentID  xid.ID  `bun:"environment_id,notnull,type:varchar(20)" json:"environmentID"`     // Required: environment-scoped key
	OrganizationID *xid.ID `bun:"organization_id,type:varchar(20)" json:"organizationID,omitempty"` // Optional: org-scoped key
	UserID         xid.ID  `bun:"user_id,notnull,type:varchar(20)" json:"userID"`                   // User who created the key

	// Key identification
	Name        string `bun:"name,notnull" json:"name"`
	Description string `bun:"description" json:"description,omitempty"`
	Prefix      string `bun:"prefix,notnull,unique" json:"prefix"`          // pk_test_xxx, sk_prod_xxx, rk_dev_xxx
	KeyType     string `bun:"key_type,notnull,default:'rk'" json:"keyType"` // pk/sk/rk
	KeyHash     string `bun:"key_hash,notnull" json:"-"`                    // Hashed key for verification

	// Permissions and scopes
	Scopes      []string          `bun:"scopes,type:jsonb" json:"scopes"`                     // ["read", "write", "admin"]
	Permissions map[string]string `bun:"permissions,type:jsonb" json:"permissions"`           // Custom permissions
	RateLimit   int               `bun:"rate_limit,default:1000" json:"rate_limit"`           // Requests per hour
	AllowedIPs  []string          `bun:"allowed_ips,type:jsonb" json:"allowed_ips,omitempty"` // IP whitelist (CIDR notation supported)

	// Status and expiration
	Active    bool       `bun:"active,notnull,default:true" json:"active"`
	ExpiresAt *time.Time `bun:"expires_at" json:"expires_at,omitempty"`

	// Usage tracking
	UsageCount int64      `bun:"usage_count,notnull,default:0" json:"usage_count"`
	LastUsedAt *time.Time `bun:"last_used_at" json:"last_used_at,omitempty"`
	LastUsedIP string     `bun:"last_used_ip" json:"last_used_ip,omitempty"`
	LastUsedUA string     `bun:"last_used_ua" json:"last_used_ua,omitempty"`

	// Metadata
	Metadata map[string]string `bun:"metadata,type:jsonb" json:"metadata,omitempty"`

	// RBAC Integration (Hybrid Approach)
	DelegateUserPermissions bool    `bun:"delegate_user_permissions,notnull,default:false" json:"delegateUserPermissions"` // Inherit creator's permissions
	ImpersonateUserID       *xid.ID `bun:"impersonate_user_id,type:varchar(20)" json:"impersonateUserID,omitempty"`        // Act as specific user

	// RBAC Relationships
	Roles []*Role `bun:"m2m:apikey_roles,join:APIKey=Role" json:"-"` // Many-to-many with roles

	// Transient field - only populated during creation
	Key string `bun:"-" json:"key,omitempty"`
}

APIKey represents an API key for programmatic access Updated for V2 architecture: App → Environment → Organization

func (*APIKey) BeforeAppendModel

func (a *APIKey) BeforeAppendModel(ctx context.Context, query bun.Query) error

BeforeAppendModel implements bun.BeforeAppendModelHook

func (*APIKey) HasPermission

func (a *APIKey) HasPermission(permission string) bool

HasPermission checks if the API key has a specific permission

func (*APIKey) HasScope

func (a *APIKey) HasScope(scope string) bool

HasScope checks if the API key has a specific scope

func (*APIKey) IsExpired

func (a *APIKey) IsExpired() bool

IsExpired checks if the API key has expired

func (*APIKey) IsIPAllowed

func (a *APIKey) IsIPAllowed(ip string) bool

IsIPAllowed checks if an IP address is in the allowed list Supports exact IP matching and CIDR notation

type APIKeyRole

type APIKeyRole struct {
	bun.BaseModel `bun:"table:apikey_roles,alias:akr"`

	ID             xid.ID     `bun:"id,pk,type:varchar(20)"`
	APIKeyID       xid.ID     `bun:"api_key_id,notnull,type:varchar(20)"`
	RoleID         xid.ID     `bun:"role_id,notnull,type:varchar(20)"`
	OrganizationID *xid.ID    `bun:"organization_id,type:varchar(20)"` // Optional: org-scoped assignment
	CreatedAt      time.Time  `bun:"created_at,notnull"`
	CreatedBy      *xid.ID    `bun:"created_by,type:varchar(20)"`
	DeletedAt      *time.Time `bun:"deleted_at"`

	// Relations
	APIKey *APIKey `bun:"rel:belongs-to,join:api_key_id=id"`
	Role   *Role   `bun:"rel:belongs-to,join:role_id=id"`
}

APIKeyRole represents the many-to-many relationship between API keys and roles This enables API keys to leverage the RBAC system for structured permissions

func (*APIKeyRole) BeforeAppendModel

func (ar *APIKeyRole) BeforeAppendModel(ctx context.Context, query bun.Query) error

BeforeAppendModel implements bun.BeforeAppendModelHook

type Account

type Account struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:accounts,alias:a"`

	ID           xid.ID     `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID        xid.ID     `json:"appID" bun:"app_id,notnull,type:varchar(20)"`
	UserID       xid.ID     `json:"userID" bun:"user_id,notnull,type:varchar(20)"`
	Provider     string     `json:"provider" bun:"provider,notnull"` // google, github, etc.
	ProviderID   string     `json:"providerID" bun:"provider_id,notnull"`
	AccessToken  string     `json:"accessToken" bun:"access_token"`
	RefreshToken string     `json:"refreshToken" bun:"refresh_token"`
	ExpiresAt    *time.Time `json:"expiresAt" bun:"expires_at"`

	// Relations
	App  *App  `bun:"rel:belongs-to,join:app_id=id"`
	User *User `bun:"rel:belongs-to,join:user_id=id"`
}

Account represents OAuth accounts

type AccountLockout

type AccountLockout struct {
	bun.BaseModel `bun:"table:account_lockouts,alias:al"`

	ID          xid.ID    `bun:"type:varchar(20),pk" json:"id"`
	UserID      xid.ID    `bun:"type:varchar(20),notnull" json:"user_id"`
	LockedUntil time.Time `bun:"type:timestamptz,notnull" json:"locked_until"`
	Reason      string    `bun:"type:varchar(255)" json:"reason"`
	CreatedAt   time.Time `bun:"type:timestamptz,notnull,default:current_timestamp" json:"created_at"`
}

AccountLockout tracks locked user accounts due to failed login attempts

type App

type App struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:apps,alias:a"`

	ID         xid.ID                 `json:"id" bun:"id,pk,type:varchar(20)"`
	Name       string                 `json:"name" bun:"name,notnull"`
	Slug       string                 `json:"slug" bun:"slug,notnull,unique"`
	Metadata   map[string]interface{} `json:"metadata" bun:"metadata,type:jsonb"`
	IsPlatform bool                   `json:"isPlatform" bun:"is_platform,default:false"` // Identifies the single platform app

	// Relations
	Members []Member `json:"members,omitempty" bun:"rel:has-many,join:id=app_id"`
	Teams   []Team   `json:"teams,omitempty" bun:"rel:has-many,join:id=app_id"`
}

App represents the app table (formerly Organization - platform-level tenant)

type AuditEvent

type AuditEvent struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:audit_events,alias:ae"`

	ID             xid.ID  `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID          xid.ID  `json:"appID" bun:"app_id,notnull,type:varchar(20)"`
	OrganizationID *xid.ID `json:"organizationID,omitempty" bun:"organization_id,type:varchar(20)"` // User-created organization (optional)
	EnvironmentID  *xid.ID `json:"environmentID" bun:"environment_id,type:varchar(20)"`             // Environment scoping
	UserID         *xid.ID `json:"userID" bun:"user_id,type:varchar(20)"`
	Action         string  `json:"action" bun:"action,notnull"`
	Resource       string  `json:"resource" bun:"resource,notnull"`
	IPAddress      string  `json:"ipAddress" bun:"ip_address"`
	UserAgent      string  `json:"userAgent" bun:"user_agent"`
	Metadata       string  `json:"metadata" bun:"metadata"`

	// Relations
	App          *App          `bun:"rel:belongs-to,join:app_id=id"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"`
	Environment  *Environment  `bun:"rel:belongs-to,join:environment_id=id"`
}

AuditEvent represents the audit_events table

type AuditableModel

type AuditableModel struct {
	ID        xid.ID     `json:"id" bun:"id,pk,type:varchar(20)"`
	CreatedAt time.Time  `json:"createdAt" bun:"created_at,nullzero,notnull,default:current_timestamp"`
	CreatedBy xid.ID     `json:"createdBy" bun:"created_by,nullzero"`
	UpdatedAt time.Time  `json:"updatedAt" bun:"updated_at,nullzero,notnull,default:current_timestamp"`
	UpdatedBy xid.ID     `json:"updatedBy" bun:"updated_by,nullzero"`
	DeletedAt *time.Time `json:"deletedAt,omitempty" bun:"deleted_at,nullzero"`
	Version   int        `json:"version" bun:"version,default:1"`
}

func (*AuditableModel) BeforeAppendModel

func (u *AuditableModel) BeforeAppendModel(ctx context.Context, query bun.Query) error

type AuthorizationCode

type AuthorizationCode struct {
	AuditableModel
	bun.BaseModel `bun:"table:authorization_codes"`

	// Context fields
	AppID          xid.ID  `bun:"app_id,notnull,type:varchar(20)" json:"appID"`
	EnvironmentID  xid.ID  `bun:"environment_id,notnull,type:varchar(20)" json:"environmentID"`
	OrganizationID *xid.ID `bun:"organization_id,type:varchar(20)" json:"organizationID,omitempty"` // Optional org context
	SessionID      *xid.ID `bun:"session_id,type:varchar(20)" json:"sessionID,omitempty"`           // Link to user session

	// OAuth2/OIDC fields
	Code                string `bun:"code,unique,notnull" json:"code"`                            // The authorization code
	ClientID            string `bun:"client_id,notnull" json:"clientID"`                          // OAuth client ID
	UserID              xid.ID `bun:"user_id,notnull,type:varchar(20)" json:"userID"`             // User who authorized
	RedirectURI         string `bun:"redirect_uri,notnull" json:"redirectURI"`                    // Redirect URI used in auth request
	Scope               string `bun:"scope,notnull" json:"scope"`                                 // Requested scopes
	State               string `bun:"state" json:"state,omitempty"`                               // State parameter from auth request
	Nonce               string `bun:"nonce" json:"nonce,omitempty"`                               // Nonce for OIDC
	CodeChallenge       string `bun:"code_challenge" json:"codeChallenge,omitempty"`              // PKCE code challenge
	CodeChallengeMethod string `bun:"code_challenge_method" json:"codeChallengeMethod,omitempty"` // PKCE challenge method (S256, plain)

	// Consent tracking
	ConsentGranted bool   `bun:"consent_granted,default:false" json:"consentGranted"`
	ConsentScopes  string `bun:"consent_scopes" json:"consentScopes,omitempty"` // Scopes user consented to

	// Authentication context
	AuthTime time.Time `bun:"auth_time,notnull" json:"authTime"` // When user authenticated (for max_age checks)

	// Lifecycle
	ExpiresAt time.Time  `bun:"expires_at,notnull" json:"expiresAt"` // Code expiration (typically 10 minutes)
	Used      bool       `bun:"used,notnull,default:false" json:"used"`
	UsedAt    *time.Time `bun:"used_at" json:"usedAt,omitempty"`

	// Relations
	App          *App          `bun:"rel:belongs-to,join:app_id=id"`
	Environment  *Environment  `bun:"rel:belongs-to,join:environment_id=id"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"`
	Session      *Session      `bun:"rel:belongs-to,join:session_id=id"`
}

AuthorizationCode represents an OAuth2/OIDC authorization code

func (*AuthorizationCode) IsExpired

func (ac *AuthorizationCode) IsExpired() bool

IsExpired checks if the authorization code has expired

func (*AuthorizationCode) IsValid

func (ac *AuthorizationCode) IsValid() bool

IsValid checks if the authorization code is valid (not expired and not used)

type BackupCode

type BackupCode struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:twofa_backup_codes,alias:tbc"`

	ID       xid.ID     `bun:"id,pk,type:varchar(20)"`
	AppID    xid.ID     `bun:"app_id,notnull,type:varchar(20)"`
	UserID   xid.ID     `bun:"user_id,notnull,type:varchar(20)"`
	CodeHash string     `bun:"code_hash,notnull"`
	UsedAt   *time.Time `bun:"used_at"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

BackupCode stores recovery codes for 2FA

type Delivery

type Delivery struct {
	bun.BaseModel `bun:"table:webhook_deliveries,alias:wd"`

	ID              xid.ID     `bun:"id,pk,type:varchar(20)" json:"id"`
	WebhookID       xid.ID     `bun:"webhook_id,notnull,type:varchar(20)" json:"webhook_id"`
	EventID         xid.ID     `bun:"event_id,notnull,type:varchar(20)" json:"event_id"`
	URL             string     `bun:"url,notnull" json:"url"`
	HTTPMethod      string     `bun:"http_method,notnull,default:'POST'" json:"http_method"`
	Headers         []byte     `bun:"headers,type:jsonb" json:"headers,omitempty"`
	Body            []byte     `bun:"body" json:"body,omitempty"`
	Status          string     `bun:"status,notnull" json:"status"`
	StatusCode      *int       `bun:"status_code" json:"status_code,omitempty"`
	ResponseHeaders []byte     `bun:"response_headers,type:jsonb" json:"response_headers,omitempty"`
	ResponseBody    []byte     `bun:"response_body" json:"response_body,omitempty"`
	Error           *string    `bun:"error" json:"error,omitempty"`
	AttemptNumber   int        `bun:"attempt_number,notnull,default:1" json:"attempt_number"`
	NextRetryAt     *time.Time `bun:"next_retry_at" json:"next_retry_at,omitempty"`
	DeliveredAt     *time.Time `bun:"delivered_at" json:"delivered_at,omitempty"`
	CreatedAt       time.Time  `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	UpdatedAt       time.Time  `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`

	// Relations
	Webhook *Webhook `bun:"rel:belongs-to,join:webhook_id=id" json:"webhook,omitempty"`
	Event   *Event   `bun:"rel:belongs-to,join:event_id=id" json:"event,omitempty"`
}

Delivery represents a webhook delivery attempt

type Device

type Device struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:devices,alias:d"`

	ID          xid.ID    `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID       xid.ID    `json:"appID" bun:"app_id,notnull,type:varchar(20)"`
	UserID      xid.ID    `json:"userID" bun:"user_id,notnull,type:varchar(20)"`
	Fingerprint string    `json:"fingerprint" bun:"fingerprint,notnull,unique"`
	UserAgent   string    `json:"userAgent" bun:"user_agent"`
	IPAddress   string    `json:"ipAddress" bun:"ip_address"`
	LastActive  time.Time `json:"lastActive" bun:"last_active,notnull"`

	// Relations
	App  *App  `bun:"rel:belongs-to,join:app_id=id"`
	User *User `bun:"rel:belongs-to,join:user_id=id"`
}

Device represents the devices table

type EmailOTP

type EmailOTP struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:email_otps,alias:eotp"`

	ID        xid.ID    `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID     xid.ID    `json:"appID" bun:"app_id,notnull,type:varchar(20)"`
	Email     string    `json:"email" bun:"email,notnull"`
	OTP       string    `json:"otp" bun:"otp,notnull"`
	ExpiresAt time.Time `json:"expiresAt" bun:"expires_at,notnull"`
	Attempts  int       `json:"attempts" bun:"attempts,notnull,default:0"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

EmailOTP stores OTP codes for email-based authentication

type Environment

type Environment struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:environments,alias:env"`

	ID        xid.ID                 `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID     xid.ID                 `json:"appID" bun:"app_id,notnull,type:varchar(20)"` // Foreign key to App
	Name      string                 `json:"name" bun:"name,notnull"`
	Slug      string                 `json:"slug" bun:"slug,notnull"`                          // dev, prod, staging
	Type      string                 `json:"type" bun:"type,notnull"`                          // development, production, staging, preview
	Status    string                 `json:"status" bun:"status,notnull,default:'active'"`     // active, inactive
	Config    map[string]interface{} `json:"config" bun:"config,type:jsonb"`                   // Environment-specific configuration
	IsDefault bool                   `json:"isDefault" bun:"is_default,notnull,default:false"` // Is this the default environment for the app

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

Environment represents an isolated data context within an App (dev, prod, staging, etc.)

func (*Environment) CanDelete

func (e *Environment) CanDelete() bool

CanDelete checks if this environment can be deleted

func (*Environment) IsDevelopment

func (e *Environment) IsDevelopment() bool

IsDevelopment checks if this is a development environment

func (*Environment) IsProduction

func (e *Environment) IsProduction() bool

IsProduction checks if this is a production environment

type EnvironmentPromotion

type EnvironmentPromotion struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:environment_promotions,alias:ep"`

	ID           xid.ID     `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID        xid.ID     `json:"appID" bun:"app_id,notnull,type:varchar(20)"`
	SourceEnvID  xid.ID     `json:"sourceEnvId" bun:"source_env_id,notnull,type:varchar(20)"`
	TargetEnvID  xid.ID     `json:"targetEnvId" bun:"target_env_id,notnull,type:varchar(20)"`
	PromotedBy   xid.ID     `json:"promotedBy" bun:"promoted_by,notnull,type:varchar(20)"` // User who performed promotion
	IncludeData  bool       `json:"includeData" bun:"include_data,notnull,default:false"`  // Whether to copy data or just schema
	Status       string     `json:"status" bun:"status,notnull"`                           // pending, in_progress, completed, failed
	ErrorMessage string     `json:"errorMessage" bun:"error_message"`
	CompletedAt  *time.Time `json:"completedAt" bun:"completed_at"`

	// Relations
	App       *App         `bun:"rel:belongs-to,join:app_id=id"`
	SourceEnv *Environment `bun:"rel:belongs-to,join:source_env_id=id"`
	TargetEnv *Environment `bun:"rel:belongs-to,join:target_env_id=id"`
}

EnvironmentPromotion represents a promotion from one environment to another

type Event

type Event struct {
	bun.BaseModel `bun:"table:webhook_events,alias:we"`

	ID            xid.ID                 `bun:"id,pk,type:varchar(20)" json:"id"`
	AppID         xid.ID                 `bun:"app_id,notnull,type:varchar(20)" json:"app_id"`
	EnvironmentID xid.ID                 `bun:"environment_id,notnull,type:varchar(20)" json:"environment_id"`
	Type          string                 `bun:"type,notnull" json:"type"`
	Data          map[string]interface{} `bun:"data,type:jsonb" json:"data"`
	UserID        *xid.ID                `bun:"user_id,type:varchar(20)" json:"user_id,omitempty"`
	SessionID     *xid.ID                `bun:"session_id,type:varchar(20)" json:"session_id,omitempty"`
	IPAddress     string                 `bun:"ip_address" json:"ip_address,omitempty"`
	UserAgent     string                 `bun:"user_agent" json:"user_agent,omitempty"`
	OccurredAt    time.Time              `bun:"occurred_at,nullzero,notnull,default:current_timestamp" json:"occurred_at"`
	CreatedAt     time.Time              `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
}

Event represents a webhook event

type FailedLoginAttempt

type FailedLoginAttempt struct {
	bun.BaseModel `bun:"table:failed_login_attempts,alias:fla"`

	ID        xid.ID    `bun:"type:varchar(20),pk" json:"id"`
	Username  string    `bun:"type:varchar(255),notnull" json:"username"`
	AppID     xid.ID    `bun:"type:varchar(20),notnull" json:"app_id"`
	IP        string    `bun:"type:varchar(45)" json:"ip"`
	UserAgent string    `bun:"type:text" json:"user_agent"`
	AttemptAt time.Time `bun:"type:timestamptz,notnull,default:current_timestamp" json:"attempt_at"`
}

FailedLoginAttempt records failed login attempts for account lockout functionality

type FormField

type FormField struct {
	ID          string                 `json:"id"`
	Type        string                 `json:"type"` // text, email, password, select, checkbox, etc.
	Label       string                 `json:"label"`
	Placeholder string                 `json:"placeholder"`
	Required    bool                   `json:"required"`
	Validation  map[string]interface{} `json:"validation"`
	Options     []string               `json:"options,omitempty"` // For select fields
	Metadata    map[string]interface{} `json:"metadata,omitempty"`
}

FormField represents a single form field configuration

type FormSchema

type FormSchema struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:form_schemas,alias:fs"`

	ID          xid.ID                 `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID       xid.ID                 `json:"appID" bun:"app_id,notnull,type:varchar(20)"` // App-scoped form schemas
	Type        string                 `json:"type" bun:"type,notnull"`                     // signup, signin, profile, etc.
	Name        string                 `json:"name" bun:"name,notnull"`
	Description string                 `json:"description" bun:"description"`
	Schema      map[string]interface{} `json:"schema" bun:"schema,type:jsonb,notnull"`
	IsActive    bool                   `json:"isActive" bun:"is_active,notnull,default:true"`
	Version     int                    `json:"version" bun:"version,notnull,default:1"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

FormSchema represents app-specific form configurations

type FormSubmission

type FormSubmission struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:form_submissions,alias:fsub"`

	ID           xid.ID                 `json:"id" bun:"id,pk,type:varchar(20)"`
	FormSchemaID xid.ID                 `json:"formSchemaId" bun:"form_schema_id,notnull,type:varchar(20)"`
	UserID       *xid.ID                `json:"userId" bun:"user_id,type:varchar(20)"` // Optional for anonymous submissions
	SessionID    *xid.ID                `json:"sessionId" bun:"session_id,type:varchar(20)"`
	Data         map[string]interface{} `json:"data" bun:"data,type:jsonb,notnull"`
	IPAddress    string                 `json:"ipAddress" bun:"ip_address"`
	UserAgent    string                 `json:"userAgent" bun:"user_agent"`
	Status       string                 `json:"status" bun:"status,notnull,default:'submitted'"` // submitted, processed, failed

	// Relations
	FormSchema *FormSchema `bun:"rel:belongs-to,join:form_schema_id=id"`
	User       *User       `bun:"rel:belongs-to,join:user_id=id"`
	Session    *Session    `bun:"rel:belongs-to,join:session_id=id"`
}

FormSubmission represents a form submission

type IDModel

type IDModel struct {
	ID xid.ID `bun:"id,pk,type:varchar(20)" json:"id"`
}

type IdentityVerification

type IdentityVerification struct {
	AuditableModel
	bun.BaseModel `bun:"table:identity_verifications,alias:iv"`

	ID string `bun:"id,pk,type:varchar(255)" json:"id"`

	// V2 Multi-tenant context (App → Environment → Organization)
	AppID          string  `bun:"app_id,notnull,type:varchar(20)" json:"appId"`
	EnvironmentID  *string `bun:"environment_id,type:varchar(20)" json:"environmentId,omitempty"`
	OrganizationID string  `bun:"organization_id,notnull,type:varchar(20)" json:"organizationId"`
	UserID         string  `bun:"user_id,notnull,type:varchar(20)" json:"userId"`

	// Provider information
	Provider        string `bun:"provider,notnull,type:varchar(50)" json:"provider"` // onfido, jumio, stripe_identity
	ProviderCheckID string `bun:"provider_check_id,type:varchar(255)" json:"providerCheckId"`

	// Verification type and status
	VerificationType string `bun:"verification_type,notnull,type:varchar(50)" json:"verificationType"` // document, liveness, age, aml
	Status           string `bun:"status,notnull,type:varchar(50)" json:"status"`                      // pending, in_progress, completed, failed, expired

	// Document information (if applicable)
	DocumentType    string `bun:"document_type,type:varchar(50)" json:"documentType,omitempty"`      // passport, drivers_license, national_id
	DocumentNumber  string `bun:"document_number,type:varchar(255)" json:"documentNumber,omitempty"` // Encrypted
	DocumentCountry string `bun:"document_country,type:varchar(2)" json:"documentCountry,omitempty"` // ISO 3166-1 alpha-2

	// Verification results
	IsVerified      bool   `bun:"is_verified,default:false" json:"isVerified"`
	RiskScore       int    `bun:"risk_score,type:int" json:"riskScore,omitempty"`             // 0-100, higher is riskier
	RiskLevel       string `bun:"risk_level,type:varchar(20)" json:"riskLevel,omitempty"`     // low, medium, high
	ConfidenceScore int    `bun:"confidence_score,type:int" json:"confidenceScore,omitempty"` // 0-100

	// Personal information extracted
	FirstName   string     `bun:"first_name,type:varchar(255)" json:"firstName,omitempty"`
	LastName    string     `bun:"last_name,type:varchar(255)" json:"lastName,omitempty"`
	DateOfBirth *time.Time `bun:"date_of_birth,type:date" json:"dateOfBirth,omitempty"`
	Age         int        `bun:"age,type:int" json:"age,omitempty"`
	Gender      string     `bun:"gender,type:varchar(20)" json:"gender,omitempty"`
	Nationality string     `bun:"nationality,type:varchar(2)" json:"nationality,omitempty"` // ISO 3166-1 alpha-2

	// AML/Sanctions screening results
	IsOnSanctionsList bool   `bun:"is_on_sanctions_list,default:false" json:"isOnSanctionsList"`
	IsPEP             bool   `bun:"is_pep,default:false" json:"isPep"` // Politically Exposed Person
	SanctionsDetails  string `bun:"sanctions_details,type:text" json:"sanctionsDetails,omitempty"`

	// Liveness detection
	LivenessScore int  `bun:"liveness_score,type:int" json:"livenessScore,omitempty"` // 0-100
	IsLive        bool `bun:"is_live,default:false" json:"isLive"`

	// Rejection/failure information
	RejectionReasons []string `bun:"rejection_reasons,type:jsonb" json:"rejectionReasons,omitempty"`
	FailureReason    string   `bun:"failure_reason,type:text" json:"failureReason,omitempty"`

	// Metadata
	Metadata     map[string]interface{} `bun:"metadata,type:jsonb" json:"metadata,omitempty"`
	ProviderData map[string]interface{} `bun:"provider_data,type:jsonb" json:"providerData,omitempty"` // Raw provider response
	IPAddress    string                 `bun:"ip_address,type:varchar(45)" json:"ipAddress,omitempty"`
	UserAgent    string                 `bun:"user_agent,type:text" json:"userAgent,omitempty"`

	// Expiry and validity
	ExpiresAt  *time.Time `bun:"expires_at,type:timestamptz" json:"expiresAt,omitempty"`
	VerifiedAt *time.Time `bun:"verified_at,type:timestamptz" json:"verifiedAt,omitempty"`

	// Webhook tracking
	WebhookDeliveryStatus string     `bun:"webhook_delivery_status,type:varchar(50)" json:"webhookDeliveryStatus,omitempty"`
	WebhookDeliveredAt    *time.Time `bun:"webhook_delivered_at,type:timestamptz" json:"webhookDeliveredAt,omitempty"`

	// Relations
	User         *User         `bun:"rel:belongs-to,join:user_id=id" json:"user,omitempty"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id" json:"organization,omitempty"`
}

IdentityVerification represents a KYC verification attempt

type IdentityVerificationDocument

type IdentityVerificationDocument struct {
	bun.BaseModel `bun:"table:identity_verification_documents,alias:ivd"`

	ID             string `bun:"id,pk,type:varchar(255)" json:"id"`
	VerificationID string `bun:"verification_id,notnull,type:varchar(255)" json:"verificationId"`

	// V2 Multi-tenant context (inherited from parent verification)
	AppID          string  `bun:"app_id,notnull,type:varchar(20)" json:"appId"`
	EnvironmentID  *string `bun:"environment_id,type:varchar(20)" json:"environmentId,omitempty"`
	OrganizationID string  `bun:"organization_id,notnull,type:varchar(20)" json:"organizationId"`

	// Document details
	DocumentSide string `bun:"document_side,type:varchar(20)" json:"documentSide"` // front, back, selfie
	FileURL      string `bun:"file_url,type:text" json:"fileUrl"`                  // Encrypted storage URL
	FileHash     string `bun:"file_hash,type:varchar(64)" json:"fileHash"`         // SHA-256 for integrity
	MimeType     string `bun:"mime_type,type:varchar(100)" json:"mimeType"`
	FileSize     int64  `bun:"file_size,type:bigint" json:"fileSize"`

	// Processing status
	ProcessingStatus string `bun:"processing_status,type:varchar(50)" json:"processingStatus"` // pending, processing, processed, failed

	// Extracted data
	ExtractedData map[string]interface{} `bun:"extracted_data,type:jsonb" json:"extractedData,omitempty"`

	// Retention policy
	RetainUntil *time.Time `bun:"retain_until,type:timestamptz" json:"retainUntil,omitempty"`
	DeletedAt   *time.Time `bun:"deleted_at,type:timestamptz" json:"deletedAt,omitempty"`

	CreatedAt time.Time `bun:"created_at,notnull,default:current_timestamp" json:"createdAt"`
	UpdatedAt time.Time `bun:"updated_at,notnull,default:current_timestamp" json:"updatedAt"`

	// Relations
	Verification *IdentityVerification `bun:"rel:belongs-to,join:verification_id=id" json:"verification,omitempty"`
}

IdentityVerificationDocument represents uploaded documents for verification

type IdentityVerificationSession

type IdentityVerificationSession struct {
	bun.BaseModel `bun:"table:identity_verification_sessions,alias:ivs"`

	ID string `bun:"id,pk,type:varchar(255)" json:"id"`

	// V2 Multi-tenant context (App → Environment → Organization)
	AppID          string  `bun:"app_id,notnull,type:varchar(20)" json:"appId"`
	EnvironmentID  *string `bun:"environment_id,type:varchar(20)" json:"environmentId,omitempty"`
	OrganizationID string  `bun:"organization_id,notnull,type:varchar(20)" json:"organizationId"`
	UserID         string  `bun:"user_id,notnull,type:varchar(20)" json:"userId"`

	// Session details
	Provider     string `bun:"provider,notnull,type:varchar(50)" json:"provider"`
	SessionURL   string `bun:"session_url,type:text" json:"sessionUrl"`             // URL for user to complete verification
	SessionToken string `bun:"session_token,type:varchar(255)" json:"sessionToken"` // Encrypted

	// Configuration
	RequiredChecks []string               `bun:"required_checks,type:jsonb" json:"requiredChecks"` // [document, liveness, aml]
	Config         map[string]interface{} `bun:"config,type:jsonb" json:"config,omitempty"`

	// Status tracking
	Status      string     `bun:"status,notnull,type:varchar(50)" json:"status"` // created, started, completed, abandoned, expired
	CompletedAt *time.Time `bun:"completed_at,type:timestamptz" json:"completedAt,omitempty"`
	ExpiresAt   time.Time  `bun:"expires_at,notnull,type:timestamptz" json:"expiresAt"`

	// Callback URLs
	SuccessURL string `bun:"success_url,type:text" json:"successUrl,omitempty"`
	CancelURL  string `bun:"cancel_url,type:text" json:"cancelUrl,omitempty"`

	// Tracking
	IPAddress string `bun:"ip_address,type:varchar(45)" json:"ipAddress,omitempty"`
	UserAgent string `bun:"user_agent,type:text" json:"userAgent,omitempty"`

	CreatedAt time.Time `bun:"created_at,notnull,default:current_timestamp" json:"createdAt"`
	UpdatedAt time.Time `bun:"updated_at,notnull,default:current_timestamp" json:"updatedAt"`

	// Relations
	User         *User         `bun:"rel:belongs-to,join:user_id=id" json:"user,omitempty"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id" json:"organization,omitempty"`
}

IdentityVerificationSession represents a verification session/flow

type ImpersonationAuditEvent

type ImpersonationAuditEvent struct {
	bun.BaseModel `bun:"table:impersonation_audit,alias:ia"`

	ID              xid.ID            `bun:"id,pk,type:varchar(20)" json:"id"`
	ImpersonationID xid.ID            `bun:"impersonation_id,notnull,type:varchar(20)" json:"impersonationID"`
	AppID           xid.ID            `bun:"app_id,notnull,type:varchar(20)" json:"appID"`                     // Platform app (required)
	EnvironmentID   *xid.ID           `bun:"environment_id,type:varchar(20)" json:"environmentID,omitempty"`   // Environment (optional)
	OrganizationID  *xid.ID           `bun:"organization_id,type:varchar(20)" json:"organizationID,omitempty"` // User-created organization (optional)
	EventType       string            `bun:"event_type,notnull,type:varchar(50)" json:"eventType"`             // started, ended, action_performed, expired
	Action          string            `bun:"action,type:varchar(100)" json:"action,omitempty"`                 // Specific action performed during impersonation
	Resource        string            `bun:"resource,type:varchar(255)" json:"resource,omitempty"`             // Resource accessed
	IPAddress       string            `bun:"ip_address,type:varchar(45)" json:"ipAddress"`
	UserAgent       string            `bun:"user_agent,type:text" json:"userAgent"`
	Details         map[string]string `bun:"details,type:jsonb" json:"details,omitempty"`
	CreatedAt       time.Time         `bun:"created_at,notnull,default:current_timestamp" json:"createdAt"`

	// Relationships
	ImpersonationSession *ImpersonationSession `bun:"rel:belongs-to,join:impersonation_id=id" json:"impersonationSession,omitempty"`
	App                  *App                  `bun:"rel:belongs-to,join:app_id=id" json:"app,omitempty"`
	Environment          *Environment          `bun:"rel:belongs-to,join:environment_id=id" json:"environment,omitempty"`
	Organization         *Organization         `bun:"rel:belongs-to,join:organization_id=id" json:"organization,omitempty"`
}

ImpersonationAuditEvent represents a detailed audit log for impersonation events Updated for V2 architecture: App → Environment → Organization

type ImpersonationSession

type ImpersonationSession struct {
	AuditableModel
	bun.BaseModel `bun:"table:impersonation_sessions,alias:is"`

	// Core fields
	AppID           xid.ID  `bun:"app_id,notnull,type:varchar(20)" json:"appID"`                       // Platform app (required)
	EnvironmentID   *xid.ID `bun:"environment_id,type:varchar(20)" json:"environmentID,omitempty"`     // Environment (optional)
	OrganizationID  *xid.ID `bun:"organization_id,type:varchar(20)" json:"organizationID,omitempty"`   // User-created organization (optional)
	ImpersonatorID  xid.ID  `bun:"impersonator_id,notnull,type:varchar(20)" json:"impersonatorID"`     // Admin who is impersonating
	TargetUserID    xid.ID  `bun:"target_user_id,notnull,type:varchar(20)" json:"targetUserID"`        // User being impersonated
	OriginalSession *xid.ID `bun:"original_session,type:varchar(20)" json:"originalSession,omitempty"` // Admin's original session
	NewSessionID    *xid.ID `bun:"new_session_id,type:varchar(20)" json:"newSessionID,omitempty"`      // New session for impersonation
	SessionToken    string  `bun:"session_token,type:text" json:"-"`                                   // Session token for revocation (not exposed in JSON)

	// Metadata
	Reason       string            `bun:"reason,type:text" json:"reason"`                                 // Required: ticket/reason for impersonation
	IPAddress    string            `bun:"ip_address,type:varchar(45)" json:"ip_address"`                  // Admin's IP
	UserAgent    string            `bun:"user_agent,type:text" json:"user_agent"`                         // Admin's user agent
	Metadata     map[string]string `bun:"metadata,type:jsonb" json:"metadata,omitempty"`                  // Additional context
	TicketNumber string            `bun:"ticket_number,type:varchar(100)" json:"ticket_number,omitempty"` // Support ticket reference

	// Status and lifecycle
	Active    bool       `bun:"active,notnull,default:true" json:"active"`               // Currently active
	ExpiresAt time.Time  `bun:"expires_at,notnull" json:"expires_at"`                    // Auto-logout time
	EndedAt   *time.Time `bun:"ended_at" json:"ended_at,omitempty"`                      // When impersonation ended
	EndReason string     `bun:"end_reason,type:varchar(50)" json:"end_reason,omitempty"` // manual, timeout, error

	// Relationships (for joins)
	Impersonator *User         `bun:"rel:belongs-to,join:impersonator_id=id" json:"impersonator,omitempty"`
	TargetUser   *User         `bun:"rel:belongs-to,join:target_user_id=id" json:"targetUser,omitempty"`
	App          *App          `bun:"rel:belongs-to,join:app_id=id" json:"app,omitempty"`                   // Platform app
	Environment  *Environment  `bun:"rel:belongs-to,join:environment_id=id" json:"environment,omitempty"`   // Environment (optional)
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id" json:"organization,omitempty"` // User-created org (optional)
}

ImpersonationSession represents an admin impersonating a user Updated for V2 architecture: App → Environment → Organization

func (*ImpersonationSession) IsActive

func (i *ImpersonationSession) IsActive() bool

IsActive checks if the impersonation session is currently active

func (*ImpersonationSession) IsExpired

func (i *ImpersonationSession) IsExpired() bool

IsExpired checks if the impersonation session has expired

type Invitation

type Invitation struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:invitations,alias:inv"`

	ID         xid.ID                 `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID      xid.ID                 `json:"appID" bun:"app_id,notnull,type:varchar(20)"`
	Email      string                 `json:"email" bun:"email,notnull"`
	Role       MemberRole             `json:"role" bun:"role,notnull"`
	InviterID  xid.ID                 `json:"inviterID" bun:"inviter_id,notnull,type:varchar(20)"`
	Token      string                 `json:"token" bun:"token,notnull,unique"`
	ExpiresAt  time.Time              `json:"expiresAt" bun:"expires_at,notnull"`
	AcceptedAt *time.Time             `json:"acceptedAt" bun:"accepted_at"`
	Status     InvitationStatus       `json:"status" bun:"status,notnull"`
	Metadata   map[string]interface{} `json:"metadata" bun:"metadata,type:jsonb"`

	// Relations
	App *App `json:"app,omitempty" bun:"rel:belongs-to,join:app_id=id"`
}

Invitation represents the app invitation table

type InvitationStatus

type InvitationStatus string

InvitationStatus represents the status of an invitation

const (
	InvitationStatusPending   InvitationStatus = "pending"
	InvitationStatusAccepted  InvitationStatus = "accepted"
	InvitationStatusExpired   InvitationStatus = "expired"
	InvitationStatusCancelled InvitationStatus = "cancelled"
	InvitationStatusDeclined  InvitationStatus = "declined"
)

func (InvitationStatus) IsValid

func (s InvitationStatus) IsValid() bool

IsValid checks if the status is valid

func (InvitationStatus) String

func (s InvitationStatus) String() string

String returns the string representation

type JSONMap

type JSONMap map[string]interface{}

JSONMap is a helper type for storing JSON data

func (*JSONMap) Scan

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

Scan implements sql.Scanner for JSONMap

func (JSONMap) Value

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

Value implements driver.Valuer for JSONMap

type JWTKey

type JWTKey struct {
	AuditableModel
	bun.BaseModel `bun:"table:jwt_keys"`

	// App context
	AppID         xid.ID `bun:"app_id,notnull,type:varchar(20)" json:"appID"`
	IsPlatformKey bool   `bun:"is_platform_key,notnull,default:false" json:"isPlatformKey"`

	// Key identification
	KeyID     string `bun:"key_id,notnull" json:"keyID"`        // Kid for JWKS (unique per app)
	Algorithm string `bun:"algorithm,notnull" json:"algorithm"` // EdDSA, RS256, etc.
	KeyType   string `bun:"key_type,notnull" json:"keyType"`    // OKP, RSA
	Curve     string `bun:"curve" json:"curve,omitempty"`       // Ed25519, P-256, etc.

	// Key material (encrypted)
	PrivateKey []byte `bun:"private_key,notnull" json:"-"`        // Encrypted private key
	PublicKey  []byte `bun:"public_key,notnull" json:"publicKey"` // Public key for JWKS

	// Key status
	Active    bool       `bun:"active,notnull,default:true" json:"active"`
	ExpiresAt *time.Time `bun:"expires_at" json:"expiresAt,omitempty"`

	// Usage tracking
	UsageCount int64      `bun:"usage_count,notnull,default:0" json:"usageCount"`
	LastUsedAt *time.Time `bun:"last_used_at" json:"lastUsedAt,omitempty"`

	// Metadata
	Name        string            `bun:"name" json:"name,omitempty"`
	Description string            `bun:"description" json:"description,omitempty"`
	Metadata    map[string]string `bun:"metadata,type:jsonb" json:"metadata,omitempty"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

JWTKey represents a JWT signing key

func (*JWTKey) BeforeAppendModel

func (k *JWTKey) BeforeAppendModel(ctx context.Context, query bun.Query) error

BeforeAppendModel implements bun.BeforeAppendModelHook

type MFAAttempt

type MFAAttempt struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:mfa_attempts,alias:mfa"`

	ID            xid.ID  `bun:"id,pk,type:varchar(20)"`
	AppID         xid.ID  `bun:"app_id,notnull,type:varchar(20)"` // App context
	UserID        xid.ID  `bun:"user_id,notnull,type:varchar(20)"`
	FactorID      *xid.ID `bun:"factor_id,type:varchar(20)"`
	ChallengeID   *xid.ID `bun:"challenge_id,type:varchar(20)"`
	Type          string  `bun:"type,notnull"` // Factor type
	Success       bool    `bun:"success,notnull"`
	FailureReason string  `bun:"failure_reason"`
	IPAddress     string  `bun:"ip_address"`
	UserAgent     string  `bun:"user_agent"`
	Metadata      JSONMap `bun:"metadata,type:jsonb"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

MFAAttempt tracks verification attempts for rate limiting and security

type MFABypass

type MFABypass struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:mfa_bypasses,alias:mfb"`

	ID        xid.ID     `bun:"id,pk,type:varchar(20)"`
	AppID     xid.ID     `bun:"app_id,notnull,type:varchar(20)"` // App context
	UserID    xid.ID     `bun:"user_id,notnull,type:varchar(20)"`
	GrantedBy xid.ID     `bun:"granted_by,notnull,type:varchar(20)"` // Admin who granted bypass
	Reason    string     `bun:"reason,notnull"`
	ExpiresAt time.Time  `bun:"expires_at,notnull"`
	RevokedAt *time.Time `bun:"revoked_at"`
	RevokedBy *xid.ID    `bun:"revoked_by,type:varchar(20)"`
	Metadata  JSONMap    `bun:"metadata,type:jsonb"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

MFABypass represents temporary MFA bypass granted by admins

type MFAChallenge

type MFAChallenge struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:mfa_challenges,alias:mfc"`

	ID          xid.ID     `bun:"id,pk,type:varchar(20)"`
	AppID       xid.ID     `bun:"app_id,notnull,type:varchar(20)"`     // App context
	SessionID   xid.ID     `bun:"session_id,notnull,type:varchar(20)"` // Links to MFA session
	UserID      xid.ID     `bun:"user_id,notnull,type:varchar(20)"`
	FactorID    xid.ID     `bun:"factor_id,notnull,type:varchar(20)"`
	Type        string     `bun:"type,notnull"`   // Factor type
	Status      string     `bun:"status,notnull"` // pending, verified, failed, expired
	CodeHash    string     `bun:"code_hash"`      // Hashed verification code
	Metadata    JSONMap    `bun:"metadata,type:jsonb"`
	Attempts    int        `bun:"attempts,notnull,default:0"`
	MaxAttempts int        `bun:"max_attempts,notnull,default:3"`
	IPAddress   string     `bun:"ip_address"`
	UserAgent   string     `bun:"user_agent"`
	ExpiresAt   time.Time  `bun:"expires_at,notnull"`
	VerifiedAt  *time.Time `bun:"verified_at"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

MFAChallenge stores active MFA verification challenges

type MFAFactor

type MFAFactor struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:mfa_factors,alias:mff"`

	ID             xid.ID     `bun:"id,pk,type:varchar(20)"`
	AppID          xid.ID     `bun:"app_id,notnull,type:varchar(20)"` // App context (required)
	UserID         xid.ID     `bun:"user_id,notnull,type:varchar(20)"`
	OrganizationID *xid.ID    `bun:"organization_id,type:varchar(20)"` // User-created org (optional)
	Type           string     `bun:"type,notnull"`                     // totp, sms, email, webauthn, backup, etc.
	Status         string     `bun:"status,notnull"`                   // pending, active, disabled, revoked
	Priority       string     `bun:"priority,notnull"`                 // primary, backup, optional
	Name           string     `bun:"name"`                             // User-friendly name
	Secret         string     `bun:"secret"`                           // Encrypted factor secret
	Metadata       JSONMap    `bun:"metadata,type:jsonb"`              // Factor-specific metadata
	LastUsedAt     *time.Time `bun:"last_used_at"`
	VerifiedAt     *time.Time `bun:"verified_at"`
	ExpiresAt      *time.Time `bun:"expires_at"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

MFAFactor stores enrolled authentication factors for users

type MFAPolicy

type MFAPolicy struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:mfa_policies,alias:mfp"`

	ID                     xid.ID      `bun:"id,pk,type:varchar(20)"`
	AppID                  xid.ID      `bun:"app_id,notnull,type:varchar(20)"`  // App context
	OrganizationID         *xid.ID     `bun:"organization_id,type:varchar(20)"` // User-created org (optional)
	Enabled                bool        `bun:"enabled,notnull,default:true"`
	RequiredFactorCount    int         `bun:"required_factor_count,notnull,default:1"`
	AllowedFactorTypes     StringArray `bun:"allowed_factor_types,type:text[]"`
	RequiredFactorTypes    StringArray `bun:"required_factor_types,type:text[]"`
	GracePeriodDays        int         `bun:"grace_period_days,notnull,default:7"`
	TrustedDeviceDays      int         `bun:"trusted_device_days,notnull,default:30"`
	StepUpRequired         bool        `bun:"step_up_required,notnull,default:false"`
	AdaptiveMFAEnabled     bool        `bun:"adaptive_mfa_enabled,notnull,default:false"`
	MaxFailedAttempts      int         `bun:"max_failed_attempts,notnull,default:5"`
	LockoutDurationMinutes int         `bun:"lockout_duration_minutes,notnull,default:30"`
	Metadata               JSONMap     `bun:"metadata,type:jsonb"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

MFAPolicy defines organization-level MFA requirements

type MFARiskAssessment

type MFARiskAssessment struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:mfa_risk_assessments,alias:mra"`

	ID          xid.ID      `bun:"id,pk,type:varchar(20)"`
	AppID       xid.ID      `bun:"app_id,notnull,type:varchar(20)"` // App context
	UserID      xid.ID      `bun:"user_id,notnull,type:varchar(20)"`
	SessionID   *xid.ID     `bun:"session_id,type:varchar(20)"`
	RiskLevel   string      `bun:"risk_level,notnull"`      // low, medium, high, critical
	RiskScore   float64     `bun:"risk_score,notnull"`      // 0-100
	Factors     StringArray `bun:"factors,type:text[]"`     // Risk factors identified
	Recommended StringArray `bun:"recommended,type:text[]"` // Recommended factor types
	IPAddress   string      `bun:"ip_address"`
	UserAgent   string      `bun:"user_agent"`
	Location    string      `bun:"location"` // Geographic location
	Metadata    JSONMap     `bun:"metadata,type:jsonb"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

MFARiskAssessment stores risk assessment results

type MFASession

type MFASession struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:mfa_sessions,alias:mfs"`

	ID              xid.ID      `bun:"id,pk,type:varchar(20)"`
	AppID           xid.ID      `bun:"app_id,notnull,type:varchar(20)"` // App context
	UserID          xid.ID      `bun:"user_id,notnull,type:varchar(20)"`
	SessionToken    string      `bun:"session_token,unique,notnull"`
	FactorsRequired int         `bun:"factors_required,notnull"`
	FactorsVerified int         `bun:"factors_verified,notnull,default:0"`
	VerifiedFactors StringArray `bun:"verified_factors,type:text[]"` // Array of factor IDs
	RiskLevel       string      `bun:"risk_level"`                   // low, medium, high, critical
	RiskScore       float64     `bun:"risk_score"`                   // 0-100
	Context         string      `bun:"context"`                      // login, transaction, step-up
	IPAddress       string      `bun:"ip_address"`
	UserAgent       string      `bun:"user_agent"`
	Metadata        JSONMap     `bun:"metadata,type:jsonb"`
	ExpiresAt       time.Time   `bun:"expires_at,notnull"`
	CompletedAt     *time.Time  `bun:"completed_at"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

MFASession represents an MFA verification session

type MFATrustedDevice

type MFATrustedDevice struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:mfa_trusted_devices,alias:mtd"`

	ID         xid.ID     `bun:"id,pk,type:varchar(20)"`
	AppID      xid.ID     `bun:"app_id,notnull,type:varchar(20)"` // App context
	UserID     xid.ID     `bun:"user_id,notnull,type:varchar(20)"`
	DeviceID   string     `bun:"device_id,notnull"`   // Fingerprint/identifier
	Name       string     `bun:"name"`                // User-friendly name
	Metadata   JSONMap    `bun:"metadata,type:jsonb"` // Device info
	IPAddress  string     `bun:"ip_address"`
	UserAgent  string     `bun:"user_agent"`
	LastUsedAt *time.Time `bun:"last_used_at"`
	ExpiresAt  time.Time  `bun:"expires_at,notnull"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

MFATrustedDevice stores trusted devices that can skip MFA

type MagicLink struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:magic_links,alias:ml"`

	ID             xid.ID    `json:"id" bun:"id,pk,type:varchar(20)"`
	Email          string    `json:"email" bun:"email,notnull"`
	Token          string    `json:"token" bun:"token,notnull"`
	AppID          xid.ID    `json:"appID" bun:"app_id,notnull,type:varchar(20)"`                     // Platform app (required)
	OrganizationID *xid.ID   `json:"organizationID,omitempty" bun:"organization_id,type:varchar(20)"` // User-created org (optional)
	EnvironmentID  *xid.ID   `json:"environmentID,omitempty" bun:"environment_id,type:varchar(20)"`   // Optional environment context
	ExpiresAt      time.Time `json:"expiresAt" bun:"expires_at,notnull"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

MagicLink stores passwordless email tokens Updated for V2 architecture: App → Environment → Organization

type Member

type Member struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:members,alias:m"`

	ID       xid.ID       `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID    xid.ID       `json:"appID" bun:"app_id,notnull,type:varchar(20)"` // App context
	UserID   xid.ID       `json:"userID" bun:"user_id,notnull,type:varchar(20)"`
	Role     MemberRole   `json:"role" bun:"role,nullzero"`
	Status   MemberStatus `json:"status" bun:"status,notnull,default:'active'"`
	JoinedAt time.Time    `json:"joinedAt" bun:"joined_at,nullzero,notnull,default:current_timestamp"` // when the member joined

	// Relations
	App   *App   `json:"app,omitempty" bun:"rel:belongs-to,join:app_id=id"`
	User  *User  `json:"user,omitempty" bun:"rel:belongs-to,join:user_id=id"`
	Teams []Team `json:"teams,omitempty" bun:"m2m:team_members,join:Member=Team"`
}

Member represents the app member table

type MemberRole

type MemberRole string

MemberRole represents the role of a member in an app

const (
	MemberRoleOwner  MemberRole = "owner"
	MemberRoleAdmin  MemberRole = "admin"
	MemberRoleMember MemberRole = "member"
)

func (MemberRole) IsValid

func (r MemberRole) IsValid() bool

IsValid checks if the role is valid

func (MemberRole) String

func (r MemberRole) String() string

String returns the string representation

type MemberStatus

type MemberStatus string

MemberStatus represents the status of a member

const (
	MemberStatusActive    MemberStatus = "active"
	MemberStatusSuspended MemberStatus = "suspended"
	MemberStatusPending   MemberStatus = "pending"
)

func (MemberStatus) IsValid

func (s MemberStatus) IsValid() bool

IsValid checks if the status is valid

func (MemberStatus) String

func (s MemberStatus) String() string

String returns the string representation

type Notification

type Notification struct {
	bun.BaseModel `bun:"table:notifications,alias:n"`

	ID          xid.ID                 `bun:"id,pk,type:varchar(20)" json:"id"`
	AppID       xid.ID                 `bun:"app_id,notnull,type:varchar(20)" json:"appId"`
	TemplateID  *xid.ID                `bun:"template_id,type:varchar(20)" json:"templateId,omitempty"`
	Type        string                 `bun:"type,notnull" json:"type"`
	Recipient   string                 `bun:"recipient,notnull" json:"recipient"`
	Subject     string                 `bun:"subject" json:"subject,omitempty"`
	Body        string                 `bun:"body,notnull" json:"body"`
	Status      string                 `bun:"status,notnull" json:"status"`
	Error       string                 `bun:"error" json:"error,omitempty"`
	ProviderID  string                 `bun:"provider_id" json:"providerId,omitempty"`
	Metadata    map[string]interface{} `bun:"metadata,type:jsonb" json:"metadata,omitempty"`
	SentAt      *time.Time             `bun:"sent_at" json:"sentAt,omitempty"`
	DeliveredAt *time.Time             `bun:"delivered_at" json:"deliveredAt,omitempty"`
	CreatedAt   time.Time              `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"createdAt"`
	UpdatedAt   time.Time              `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updatedAt"`

	// Relations
	Template *NotificationTemplate `bun:"rel:belongs-to,join:template_id=id" json:"template,omitempty"`
}

Notification represents a notification instance in the database

type NotificationAnalytics

type NotificationAnalytics struct {
	bun.BaseModel `bun:"table:notification_analytics,alias:na"`

	ID             xid.ID                 `bun:"id,pk,type:varchar(20)" json:"id"`
	NotificationID xid.ID                 `bun:"notification_id,notnull,type:varchar(20)" json:"notificationId"`
	TemplateID     *xid.ID                `bun:"template_id,type:varchar(20)" json:"templateId,omitempty"`
	AppID          xid.ID                 `bun:"app_id,notnull,type:varchar(20)" json:"appId"`
	OrganizationID *xid.ID                `bun:"organization_id,type:varchar(20)" json:"organizationId,omitempty"`
	Event          string                 `bun:"event,notnull" json:"event"`                       // sent, delivered, opened, clicked, converted, bounced, complained
	EventData      map[string]interface{} `bun:"event_data,type:jsonb" json:"eventData,omitempty"` // Additional event-specific data (e.g., link clicked, conversion value)
	UserAgent      string                 `bun:"user_agent" json:"userAgent,omitempty"`
	IPAddress      string                 `bun:"ip_address" json:"ipAddress,omitempty"`
	Metadata       map[string]interface{} `bun:"metadata,type:jsonb" json:"metadata,omitempty"`
	CreatedAt      time.Time              `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"createdAt"`

	// Relations
	Notification *Notification         `bun:"rel:belongs-to,join:notification_id=id" json:"notification,omitempty"`
	Template     *NotificationTemplate `bun:"rel:belongs-to,join:template_id=id" json:"template,omitempty"`
	App          *App                  `bun:"rel:belongs-to,join:app_id=id" json:"app,omitempty"`
	Organization *Organization         `bun:"rel:belongs-to,join:organization_id=id" json:"organization,omitempty"`
}

NotificationAnalytics represents a single analytics event for a notification

type NotificationEvent

type NotificationEvent string

NotificationEvent represents the type of analytics event

const (
	NotificationEventSent       NotificationEvent = "sent"       // Notification sent
	NotificationEventDelivered  NotificationEvent = "delivered"  // Provider confirmed delivery
	NotificationEventOpened     NotificationEvent = "opened"     // Recipient opened (email tracking pixel)
	NotificationEventClicked    NotificationEvent = "clicked"    // Recipient clicked link (tracked URL)
	NotificationEventConverted  NotificationEvent = "converted"  // Recipient completed desired action
	NotificationEventBounced    NotificationEvent = "bounced"    // Delivery failed permanently
	NotificationEventComplained NotificationEvent = "complained" // Recipient marked as spam
	NotificationEventFailed     NotificationEvent = "failed"     // General failure
)

type NotificationProvider

type NotificationProvider struct {
	bun.BaseModel `bun:"table:notification_providers,alias:np"`

	ID             xid.ID                 `bun:"id,pk,type:varchar(20)" json:"id"`
	AppID          xid.ID                 `bun:"app_id,notnull,type:varchar(20)" json:"appId"`
	OrganizationID *xid.ID                `bun:"organization_id,type:varchar(20)" json:"organizationId,omitempty"` // Nullable for app-level providers
	ProviderType   string                 `bun:"provider_type,notnull" json:"providerType"`                        // email, sms, push
	ProviderName   string                 `bun:"provider_name,notnull" json:"providerName"`                        // smtp, sendgrid, twilio, etc.
	Config         map[string]interface{} `bun:"config,type:jsonb,notnull" json:"config"`                          // Encrypted provider configuration
	IsActive       bool                   `bun:"is_active,notnull,default:true" json:"isActive"`
	IsDefault      bool                   `bun:"is_default,notnull,default:false" json:"isDefault"` // Default provider for this type
	Metadata       map[string]interface{} `bun:"metadata,type:jsonb" json:"metadata,omitempty"`
	CreatedAt      time.Time              `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"createdAt"`
	UpdatedAt      time.Time              `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updatedAt"`
	DeletedAt      *time.Time             `bun:"deleted_at,soft_delete,nullzero" json:"-"`

	// Relations
	App          *App          `bun:"rel:belongs-to,join:app_id=id" json:"app,omitempty"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id" json:"organization,omitempty"`
}

NotificationProvider represents a notification provider configuration in the database Providers can be configured at app-level or organization-level

type NotificationQueue added in v0.0.7

type NotificationQueue struct {
	bun.BaseModel `bun:"table:notification_queue,alias:nq"`

	ID    xid.ID `bun:"id,pk,type:varchar(20)" json:"id"`
	AppID xid.ID `bun:"app_id,notnull,type:varchar(20)" json:"appId"`

	// Notification details
	Type        string `bun:"type,notnull" json:"type"`                  // email, sms, push
	Priority    string `bun:"priority,notnull" json:"priority"`          // critical, high, normal, low
	Recipient   string `bun:"recipient,notnull" json:"recipient"`        // Email address or phone number
	Subject     string `bun:"subject" json:"subject,omitempty"`          // Email subject
	Body        string `bun:"body" json:"body,omitempty"`                // Direct body content
	TemplateKey string `bun:"template_key" json:"templateKey,omitempty"` // Template key if using template

	// Payload for retry (JSON serialized request)
	Payload []byte `bun:"payload,type:bytea" json:"payload,omitempty"`

	// Retry state
	Attempts    int                     `bun:"attempts,notnull,default:0" json:"attempts"`
	MaxAttempts int                     `bun:"max_attempts,notnull,default:3" json:"maxAttempts"`
	LastError   string                  `bun:"last_error" json:"lastError,omitempty"`
	Status      NotificationQueueStatus `bun:"status,notnull,default:'pending'" json:"status"`
	NextRetryAt *time.Time              `bun:"next_retry_at" json:"nextRetryAt,omitempty"`
	ProcessedAt *time.Time              `bun:"processed_at" json:"processedAt,omitempty"`

	// Audit fields
	CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"createdAt"`
	UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updatedAt"`
}

NotificationQueue represents a notification queued for retry in the database

type NotificationQueueStats added in v0.0.7

type NotificationQueueStats struct {
	PendingCount    int64 `json:"pendingCount"`
	ProcessingCount int64 `json:"processingCount"`
	SucceededCount  int64 `json:"succeededCount"`
	FailedCount     int64 `json:"failedCount"`
	TotalCount      int64 `json:"totalCount"`
}

NotificationQueueStats holds aggregate statistics for the notification queue

type NotificationQueueStatus added in v0.0.7

type NotificationQueueStatus string

NotificationQueueStatus represents the status of a queued notification

const (
	NotificationQueueStatusPending    NotificationQueueStatus = "pending"
	NotificationQueueStatusProcessing NotificationQueueStatus = "processing"
	NotificationQueueStatusSucceeded  NotificationQueueStatus = "succeeded"
	NotificationQueueStatusFailed     NotificationQueueStatus = "failed"
)

type NotificationTemplate

type NotificationTemplate struct {
	bun.BaseModel `bun:"table:notification_templates,alias:nt"`

	ID             xid.ID                 `bun:"id,pk,type:varchar(20)" json:"id"`
	AppID          xid.ID                 `bun:"app_id,notnull,type:varchar(20)" json:"appId"`
	OrganizationID *xid.ID                `bun:"organization_id,type:varchar(20)" json:"organizationId,omitempty"` // Nullable for app-level templates
	TemplateKey    string                 `bun:"template_key,notnull" json:"templateKey"`                          // e.g., "auth.welcome", "auth.mfa_code"
	Name           string                 `bun:"name,notnull" json:"name"`
	Type           string                 `bun:"type,notnull" json:"type"`
	Language       string                 `bun:"language,notnull,default:'en'" json:"language"`
	Subject        string                 `bun:"subject" json:"subject,omitempty"`
	Body           string                 `bun:"body,notnull" json:"body"`
	Variables      []string               `bun:"variables,array" json:"variables"`
	Metadata       map[string]interface{} `bun:"metadata,type:jsonb" json:"metadata,omitempty"`
	Active         bool                   `bun:"active,notnull,default:true" json:"active"`
	IsDefault      bool                   `bun:"is_default,notnull,default:false" json:"isDefault"`   // Is this a default template
	IsModified     bool                   `bun:"is_modified,notnull,default:false" json:"isModified"` // Has it been modified from default
	DefaultHash    string                 `bun:"default_hash" json:"defaultHash"`                     // Hash of default content for comparison

	// Versioning fields
	Version  int     `bun:"version,notnull,default:1" json:"version"`             // Current version number
	ParentID *xid.ID `bun:"parent_id,type:varchar(20)" json:"parentId,omitempty"` // ID of template this was cloned from

	// A/B Testing fields
	ABTestGroup   string `bun:"ab_test_group" json:"abTestGroup,omitempty"`                 // Group identifier for variants
	ABTestEnabled bool   `bun:"ab_test_enabled,notnull,default:false" json:"abTestEnabled"` // Is this variant active in A/B test
	ABTestWeight  int    `bun:"ab_test_weight,notnull,default:100" json:"abTestWeight"`     // Weight for variant selection (0-100)

	// Analytics fields
	SendCount       int64 `bun:"send_count,notnull,default:0" json:"sendCount"`             // Total sends
	OpenCount       int64 `bun:"open_count,notnull,default:0" json:"openCount"`             // Total opens
	ClickCount      int64 `bun:"click_count,notnull,default:0" json:"clickCount"`           // Total clicks
	ConversionCount int64 `bun:"conversion_count,notnull,default:0" json:"conversionCount"` // Total conversions

	CreatedAt time.Time  `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"createdAt"`
	UpdatedAt time.Time  `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updatedAt"`
	DeletedAt *time.Time `bun:"deleted_at,soft_delete,nullzero" json:"-"`
}

NotificationTemplate represents a notification template in the database

type NotificationTemplateVersion

type NotificationTemplateVersion struct {
	bun.BaseModel `bun:"table:notification_template_versions,alias:ntv"`

	ID         xid.ID                 `bun:"id,pk,type:varchar(20)" json:"id"`
	TemplateID xid.ID                 `bun:"template_id,notnull,type:varchar(20)" json:"templateId"`
	Version    int                    `bun:"version,notnull" json:"version"` // Version number
	Subject    string                 `bun:"subject" json:"subject,omitempty"`
	Body       string                 `bun:"body,notnull" json:"body"`
	Variables  []string               `bun:"variables,array" json:"variables"`
	Changes    string                 `bun:"changes" json:"changes,omitempty"`                       // Description of what changed
	ChangedBy  *xid.ID                `bun:"changed_by,type:varchar(20)" json:"changedBy,omitempty"` // User who made the change
	Metadata   map[string]interface{} `bun:"metadata,type:jsonb" json:"metadata,omitempty"`
	CreatedAt  time.Time              `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"createdAt"`
	RestoredAt *time.Time             `bun:"restored_at" json:"restoredAt,omitempty"` // When this version was restored (if ever)

	// Relations
	Template *NotificationTemplate `bun:"rel:belongs-to,join:template_id=id" json:"template,omitempty"`
	User     *User                 `bun:"rel:belongs-to,join:changed_by=id" json:"user,omitempty"`
}

NotificationTemplateVersion represents a version snapshot of a notification template

type NotificationTest

type NotificationTest struct {
	bun.BaseModel `bun:"table:notification_tests,alias:ntest"`

	ID             xid.ID                 `bun:"id,pk,type:varchar(20)" json:"id"`
	TemplateID     xid.ID                 `bun:"template_id,notnull,type:varchar(20)" json:"templateId"`
	AppID          xid.ID                 `bun:"app_id,notnull,type:varchar(20)" json:"appId"`
	OrganizationID *xid.ID                `bun:"organization_id,type:varchar(20)" json:"organizationId,omitempty"`
	TestType       string                 `bun:"test_type,notnull" json:"testType"`               // preview, send, bulk
	Recipients     []string               `bun:"recipients,array" json:"recipients"`              // Test recipient(s)
	Variables      map[string]interface{} `bun:"variables,type:jsonb" json:"variables,omitempty"` // Test variables
	Results        map[string]interface{} `bun:"results,type:jsonb" json:"results,omitempty"`     // Test results (success/failure for each recipient)
	Status         string                 `bun:"status,notnull,default:'pending'" json:"status"`  // pending, running, completed, failed, partial
	Error          string                 `bun:"error" json:"error,omitempty"`                    // Error message if failed
	SuccessCount   int                    `bun:"success_count,notnull,default:0" json:"successCount"`
	FailureCount   int                    `bun:"failure_count,notnull,default:0" json:"failureCount"`
	CreatedBy      *xid.ID                `bun:"created_by,type:varchar(20)" json:"createdBy,omitempty"` // User who initiated the test
	Metadata       map[string]interface{} `bun:"metadata,type:jsonb" json:"metadata,omitempty"`
	CreatedAt      time.Time              `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"createdAt"`
	CompletedAt    *time.Time             `bun:"completed_at" json:"completedAt,omitempty"`

	// Relations
	Template     *NotificationTemplate `bun:"rel:belongs-to,join:template_id=id" json:"template,omitempty"`
	App          *App                  `bun:"rel:belongs-to,join:app_id=id" json:"app,omitempty"`
	Organization *Organization         `bun:"rel:belongs-to,join:organization_id=id" json:"organization,omitempty"`
	User         *User                 `bun:"rel:belongs-to,join:created_by=id" json:"user,omitempty"`
}

NotificationTest represents a test execution for notification templates

type NotificationTestStatus

type NotificationTestStatus string

NotificationTestStatus represents the test execution status

const (
	NotificationTestStatusPending   NotificationTestStatus = "pending"   // Test queued
	NotificationTestStatusRunning   NotificationTestStatus = "running"   // Test in progress
	NotificationTestStatusCompleted NotificationTestStatus = "completed" // Test completed successfully
	NotificationTestStatusFailed    NotificationTestStatus = "failed"    // Test failed
	NotificationTestStatusPartial   NotificationTestStatus = "partial"   // Some tests succeeded, some failed
)

type NotificationTestType

type NotificationTestType string

NotificationTestType represents the type of test being performed

const (
	NotificationTestTypePreview NotificationTestType = "preview" // Template preview/render test
	NotificationTestTypeSend    NotificationTestType = "send"    // Single test send
	NotificationTestTypeBulk    NotificationTestType = "bulk"    // Bulk test with multiple recipients
)

type OAuthClient

type OAuthClient struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:oauth_clients,alias:oc"`

	ID             xid.ID  `bun:"id,pk,type:varchar(20)" json:"id"`
	AppID          xid.ID  `bun:"app_id,notnull,type:varchar(20)" json:"appID"`
	EnvironmentID  xid.ID  `bun:"environment_id,notnull,type:varchar(20)" json:"environmentID"`
	OrganizationID *xid.ID `bun:"organization_id,type:varchar(20)" json:"organizationID,omitempty"` // null = app-level
	Name           string  `bun:"name,notnull" json:"name"`
	ClientID       string  `bun:"client_id,notnull,unique" json:"clientID"`
	ClientSecret   string  `bun:"client_secret,notnull" json:"-"`

	// OAuth2/OIDC Configuration
	RedirectURI            string   `bun:"redirect_uri,notnull" json:"redirectURI"` // Legacy single URI, kept for backward compatibility
	RedirectURIs           []string `bun:"redirect_uris,array,type:text[]" json:"redirectURIs"`
	PostLogoutRedirectURIs []string `bun:"post_logout_redirect_uris,array,type:text[]" json:"postLogoutRedirectURIs,omitempty"`
	GrantTypes             []string `bun:"grant_types,array,type:text[]" json:"grantTypes"`       // Default: ["authorization_code", "refresh_token"] - set in application code
	ResponseTypes          []string `bun:"response_types,array,type:text[]" json:"responseTypes"` // Default: ["code"] - set in application code
	AllowedScopes          []string `bun:"allowed_scopes,array,type:text[]" json:"allowedScopes,omitempty"`

	// Client Authentication & Security
	TokenEndpointAuthMethod string `bun:"token_endpoint_auth_method,default:'client_secret_basic'" json:"tokenEndpointAuthMethod"` // client_secret_basic, client_secret_post, none
	ApplicationType         string `bun:"application_type,default:'web'" json:"applicationType"`                                   // web, native, spa
	RequirePKCE             bool   `bun:"require_pkce,default:false" json:"requirePKCE"`
	RequireConsent          bool   `bun:"require_consent,default:true" json:"requireConsent"`
	TrustedClient           bool   `bun:"trusted_client,default:false" json:"trustedClient"`

	// Client Metadata (RFC 7591)
	LogoURI   string   `bun:"logo_uri" json:"logoURI,omitempty"`
	PolicyURI string   `bun:"policy_uri" json:"policyURI,omitempty"`
	TosURI    string   `bun:"tos_uri" json:"tosURI,omitempty"`
	Contacts  []string `bun:"contacts,array,type:text[]" json:"contacts,omitempty"`

	// Flexible metadata storage
	Metadata map[string]interface{} `bun:"metadata,type:jsonb" json:"metadata,omitempty"`

	// Relations
	App          *App          `bun:"rel:belongs-to,join:app_id=id"`
	Environment  *Environment  `bun:"rel:belongs-to,join:environment_id=id"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"`
}

OAuthClient stores registered OAuth/OIDC clients

type OAuthConsent

type OAuthConsent struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:oauth_consents,alias:oc"`

	ID             xid.ID  `bun:"id,pk,type:varchar(20)" json:"id"`
	AppID          xid.ID  `bun:"app_id,notnull,type:varchar(20)" json:"appID"`
	EnvironmentID  xid.ID  `bun:"environment_id,notnull,type:varchar(20)" json:"environmentID"`
	OrganizationID *xid.ID `bun:"organization_id,type:varchar(20)" json:"organizationID,omitempty"` // Optional org context
	UserID         xid.ID  `bun:"user_id,notnull,type:varchar(20)" json:"userID"`
	ClientID       string  `bun:"client_id,notnull" json:"clientID"`

	// Consent details
	Scopes    []string   `bun:"scopes,array,type:text[],notnull" json:"scopes"` // Granted scopes
	ExpiresAt *time.Time `bun:"expires_at" json:"expiresAt,omitempty"`          // Optional consent expiration

	// Relations
	App          *App          `bun:"rel:belongs-to,join:app_id=id"`
	Environment  *Environment  `bun:"rel:belongs-to,join:environment_id=id"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"`
	User         *User         `bun:"rel:belongs-to,join:user_id=id"`
}

OAuthConsent stores persistent user consent decisions for OAuth clients

func (*OAuthConsent) HasScope

func (oc *OAuthConsent) HasScope(scope string) bool

HasScope checks if a specific scope was granted

func (*OAuthConsent) IsExpired

func (oc *OAuthConsent) IsExpired() bool

IsExpired checks if the consent has expired

func (*OAuthConsent) IsValid

func (oc *OAuthConsent) IsValid() bool

IsValid checks if the consent is still valid

type OAuthToken

type OAuthToken struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:oauth_tokens,alias:ot"`

	AppID          xid.ID  `bun:"app_id,notnull,type:varchar(20)" json:"appID"`
	EnvironmentID  xid.ID  `bun:"environment_id,notnull,type:varchar(20)" json:"environmentID"`
	OrganizationID *xid.ID `bun:"organization_id,type:varchar(20)" json:"organizationID,omitempty"` // Optional org context
	SessionID      *xid.ID `bun:"session_id,type:varchar(20)" json:"sessionID,omitempty"`           // Linked session for lifecycle management

	// Token fields
	AccessToken  string `bun:"access_token,unique,notnull" json:"-"`                 // The access token
	RefreshToken string `bun:"refresh_token,unique" json:"-"`                        // Optional refresh token
	IDToken      string `bun:"id_token" json:"-"`                                    // OIDC ID token
	TokenType    string `bun:"token_type,notnull,default:'Bearer'" json:"tokenType"` // Token type (Bearer)
	TokenClass   string `bun:"token_class,default:'access_token'" json:"tokenClass"` // access_token, refresh_token, id_token
	ClientID     string `bun:"client_id,notnull" json:"clientID"`                    // OAuth client ID
	UserID       xid.ID `bun:"user_id,notnull,type:varchar(20)" json:"userID"`       // User who owns the token
	Scope        string `bun:"scope,notnull" json:"scope"`                           // Granted scopes

	// JWT Claims
	JTI       string     `bun:"jti,unique" json:"jti,omitempty"`                      // JWT ID for token revocation by ID
	Issuer    string     `bun:"issuer" json:"issuer,omitempty"`                       // Token issuer
	Audience  []string   `bun:"audience,array,type:text[]" json:"audience,omitempty"` // Token audience (aud claim)
	NotBefore *time.Time `bun:"not_before" json:"notBefore,omitempty"`                // Token validity start time (nbf claim)

	// Authentication context
	AuthTime *time.Time `bun:"auth_time" json:"authTime,omitempty"`        // When user authenticated
	ACR      string     `bun:"acr" json:"acr,omitempty"`                   // Authentication context class reference
	AMR      []string   `bun:"amr,array,type:text[]" json:"amr,omitempty"` // Authentication methods references

	// Lifecycle
	ExpiresAt        time.Time  `bun:"expires_at,notnull" json:"expiresAt"`                  // Token expiration
	RefreshExpiresAt *time.Time `bun:"refresh_expires_at" json:"refreshExpiresAt,omitempty"` // Refresh token expiration
	Revoked          bool       `bun:"revoked,notnull,default:false" json:"revoked"`         // Whether token is revoked
	RevokedAt        *time.Time `bun:"revoked_at" json:"revokedAt,omitempty"`                // When token was revoked

	// Relations
	App          *App          `bun:"rel:belongs-to,join:app_id=id"`
	Environment  *Environment  `bun:"rel:belongs-to,join:environment_id=id"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"`
	Session      *Session      `bun:"rel:belongs-to,join:session_id=id"`
}

OAuthToken represents an OAuth2/OIDC access token

func (*OAuthToken) IsExpired

func (ot *OAuthToken) IsExpired() bool

IsExpired checks if the access token has expired

func (*OAuthToken) IsRefreshValid

func (ot *OAuthToken) IsRefreshValid() bool

IsRefreshValid checks if the refresh token is valid

func (*OAuthToken) IsValid

func (ot *OAuthToken) IsValid() bool

IsValid checks if the access token is valid (not expired and not revoked)

type OTPCode

type OTPCode struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:twofa_otpcodes,alias:toc"`

	ID        xid.ID    `bun:"id,pk,type:varchar(20)"`
	AppID     xid.ID    `bun:"app_id,notnull,type:varchar(20)"`
	UserID    xid.ID    `bun:"user_id,notnull,type:varchar(20)"`
	CodeHash  string    `bun:"code_hash,notnull"`
	ExpiresAt time.Time `bun:"expires_at,notnull"`
	Attempts  int       `bun:"attempts,notnull,default:0"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

OTPCode stores one-time codes for OTP-based 2FA

type Organization

type Organization struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:organizations,alias:uo"`

	ID            xid.ID                 `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID         xid.ID                 `json:"appID" bun:"app_id,notnull,type:varchar(20)"`
	EnvironmentID xid.ID                 `json:"environmentID" bun:"environment_id,notnull,type:varchar(20)"` // Foreign key to Environment
	Name          string                 `json:"name" bun:"name,notnull"`
	Slug          string                 `json:"slug" bun:"slug,notnull"` // Unique within app+environment
	Metadata      map[string]interface{} `json:"metadata" bun:"metadata,type:jsonb"`
	CreatedBy     xid.ID                 `json:"createdBy" bun:"created_by,notnull,type:varchar(20)"`

	// Relations
	App         *App         `json:"app" bun:"rel:belongs-to,join:app_id=id"`
	Environment *Environment `json:"environment" bun:"rel:belongs-to,join:environment_id=id"`
}

Organization represents user-created organizations (Clerk-style workspaces) within an environment

type OrganizationInvitation

type OrganizationInvitation struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:organization_invitations,alias:uoi"`

	ID             xid.ID     `json:"id" bun:"id,pk,type:varchar(20)"`
	OrganizationID xid.ID     `json:"organizationID" bun:"organization_id,notnull,type:varchar(20)"`
	Email          string     `json:"email" bun:"email,notnull"`
	Role           string     `json:"role" bun:"role,notnull"`
	InviterID      xid.ID     `json:"inviterID" bun:"inviter_id,notnull,type:varchar(20)"`
	Token          string     `json:"token" bun:"token,notnull,unique"`
	ExpiresAt      time.Time  `json:"expiresAt" bun:"expires_at,notnull"`
	AcceptedAt     *time.Time `json:"acceptedAt" bun:"accepted_at"`
	Status         string     `json:"status" bun:"status,notnull"` // pending, accepted, expired, cancelled

	// Relations
	Organization *Organization `json:"organization" bun:"rel:belongs-to,join:organization_id=id"`
}

OrganizationInvitation represents invitations to user-created organizations

type OrganizationMember

type OrganizationMember struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:organization_members,alias:uom"`

	ID             xid.ID    `json:"id" bun:"id,pk,type:varchar(20)"`
	OrganizationID xid.ID    `json:"organizationID" bun:"organization_id,notnull,type:varchar(20)"`
	UserID         xid.ID    `json:"userID" bun:"user_id,notnull,type:varchar(20)"`
	Role           string    `json:"role" bun:"role,notnull"`                      // owner, admin, member
	Status         string    `json:"status" bun:"status,notnull,default:'active'"` // active, suspended, pending
	JoinedAt       time.Time `json:"joinedAt" bun:"joined_at,nullzero,notnull,default:current_timestamp"`

	// Relations
	Organization *Organization `json:"organization" bun:"rel:belongs-to,join:organization_id=id"`
	User         *User         `json:"user" bun:"rel:belongs-to,join:user_id=id"`
}

OrganizationMember represents membership in user-created organizations

type OrganizationTeam

type OrganizationTeam struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:organization_teams,alias:uot"`

	ID             xid.ID                 `json:"id" bun:"id,pk,type:varchar(20)"`
	OrganizationID xid.ID                 `json:"organizationID" bun:"organization_id,notnull,type:varchar(20)"`
	Name           string                 `json:"name" bun:"name,notnull"`
	Description    string                 `json:"description" bun:"description"`
	Metadata       map[string]interface{} `json:"metadata" bun:"metadata,type:jsonb"`

	// Provisioning tracking
	ProvisionedBy *string `json:"provisionedBy,omitempty" bun:"provisioned_by,type:varchar(50)"` // e.g., "scim"
	ExternalID    *string `json:"externalID,omitempty" bun:"external_id,type:varchar(255)"`      // External system ID

	// Relations
	Organization *Organization `json:"organization" bun:"rel:belongs-to,join:organization_id=id"`
}

OrganizationTeam represents teams within user-created organizations

type OrganizationTeamMember

type OrganizationTeamMember struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:organization_team_members,alias:uotm"`

	ID       xid.ID    `json:"id" bun:"id,pk,type:varchar(20)"`
	TeamID   xid.ID    `json:"teamID" bun:"team_id,notnull,type:varchar(20)"`
	MemberID xid.ID    `json:"memberID" bun:"member_id,notnull,type:varchar(20)"` // References OrganizationMember
	JoinedAt time.Time `json:"joinedAt" bun:"joined_at,nullzero,notnull,default:current_timestamp"`

	// Provisioning tracking
	ProvisionedBy *string `json:"provisionedBy,omitempty" bun:"provisioned_by,type:varchar(50)"` // e.g., "scim"

	// Relations
	Team   *OrganizationTeam   `json:"team" bun:"rel:belongs-to,join:team_id=id"`
	Member *OrganizationMember `json:"member" bun:"rel:belongs-to,join:member_id=id"`
}

OrganizationTeamMember represents team membership within user-created organizations

type Passkey

type Passkey struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:passkeys,alias:pk"`

	ID           xid.ID `json:"id" bun:"id,pk,type:varchar(20)"`
	UserID       xid.ID `json:"userId" bun:"user_id,notnull,type:varchar(20)"`
	CredentialID string `json:"credentialId" bun:"credential_id,notnull,unique"`

	// WebAuthn cryptographic fields
	PublicKey []byte `json:"-" bun:"public_key,notnull"`           // COSE encoded public key
	AAGUID    []byte `json:"aaguid,omitempty" bun:"aaguid"`        // Authenticator AAGUID
	SignCount uint32 `json:"signCount" bun:"sign_count,default:0"` // Counter for replay attack detection

	// Authenticator metadata
	AuthenticatorType string `json:"authenticatorType,omitempty" bun:"authenticator_type"` // "platform" or "cross-platform"

	// User-friendly naming for device management
	Name string `json:"name,omitempty" bun:"name"`

	// Resident key / discoverable credential support
	IsResidentKey bool `json:"isResidentKey" bun:"is_resident_key,default:false"`

	// Usage tracking
	LastUsedAt *time.Time `json:"lastUsedAt,omitempty" bun:"last_used_at"`

	// Multi-tenant scoping (App → Environment → Organization)
	AppID              xid.ID  `json:"appId" bun:"app_id,notnull,type:varchar(20)"`                              // Platform app (required)
	UserOrganizationID *xid.ID `json:"userOrganizationId,omitempty" bun:"user_organization_id,type:varchar(20)"` // User-created org (optional)
}

Passkey stores WebAuthn/FIDO2 credentials Updated for V2 architecture: App → Environment → Organization Now includes full WebAuthn fields for production-ready implementation

type PasswordHistory

type PasswordHistory struct {
	bun.BaseModel `bun:"table:password_histories,alias:ph"`

	ID           xid.ID    `bun:"type:varchar(20),pk" json:"id"`
	UserID       xid.ID    `bun:"type:varchar(20),notnull" json:"user_id"`
	PasswordHash string    `bun:"type:text,notnull" json:"password_hash"`
	CreatedAt    time.Time `bun:"type:timestamptz,notnull,default:current_timestamp" json:"created_at"`
}

PasswordHistory tracks user password history to prevent password reuse

type Permission

type Permission struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:permissions,alias:perm"`

	ID             xid.ID  `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID          *xid.ID `json:"appID" bun:"app_id,type:varchar(20)"`                   // App-scoped permissions
	OrganizationID *xid.ID `json:"organizationID" bun:"organization_id,type:varchar(20)"` // Org-scoped permissions (NULL = app-level)
	Name           string  `json:"name" bun:"name,notnull"`
	Description    string  `json:"description" bun:"description"`
	IsCustom       bool    `json:"isCustom" bun:"is_custom,notnull,default:false"` // Distinguishes custom from pre-defined permissions
	Category       string  `json:"category" bun:"category"`                        // Groups permissions: "users", "settings", "content", etc.

	// Relations
	App          *App          `bun:"rel:belongs-to,join:app_id=id"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"`
	Roles        []Role        `bun:"m2m:role_permissions,join:Permission=Role"`
}

Permission table

type PhoneVerification

type PhoneVerification struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:phone_verifications,alias:pver"`

	ID                 xid.ID    `json:"id" bun:"id,pk,type:varchar(20)"`
	Phone              string    `json:"phone" bun:"phone,notnull"`
	Code               string    `json:"code" bun:"code,notnull"`
	AppID              xid.ID    `json:"appId" bun:"app_id,notnull,type:varchar(20)"`                              // Platform app (required)
	UserOrganizationID *xid.ID   `json:"userOrganizationId,omitempty" bun:"user_organization_id,type:varchar(20)"` // User-created org (optional)
	ExpiresAt          time.Time `json:"expiresAt" bun:"expires_at,notnull"`
	Attempts           int       `json:"attempts" bun:"attempts,notnull,default:0"`
}

PhoneVerification stores SMS verification codes Updated for V2 architecture: App → Environment → Organization

type Policy

type Policy struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:policies,alias:pol"`

	ID         xid.ID `bun:"id,pk,type:varchar(20)"`
	Expression string `bun:"expression,notnull"`
}

Policy stores RBAC policy expressions

type Role

type Role struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:roles,alias:r"`

	ID             xid.ID  `bun:"id,pk,type:varchar(20)"`
	AppID          *xid.ID `bun:"app_id,type:varchar(20)"`          // App-scoped roles
	EnvironmentID  *xid.ID `bun:"environment_id,type:varchar(20)"`  // Environment-scoped roles
	OrganizationID *xid.ID `bun:"organization_id,type:varchar(20)"` // Org-scoped roles (NULL = app-level template)
	Name           string  `bun:"name,notnull"`                     // Slug/identifier (e.g., "workspace_owner")
	DisplayName    string  `bun:"display_name,notnull"`             // Human-readable name (e.g., "Workspace Owner")
	Description    string  `bun:"description"`
	IsTemplate     bool    `bun:"is_template,notnull,default:false"`   // Marks roles as templates for cloning
	IsOwnerRole    bool    `bun:"is_owner_role,notnull,default:false"` // Marks the default owner role for new orgs
	TemplateID     *xid.ID `bun:"template_id,type:varchar(20)"`        // Tracks which template this role was cloned from

	// Relations
	App          *App          `bun:"rel:belongs-to,join:app_id=id"`
	Environment  *Environment  `bun:"rel:belongs-to,join:environment_id=id"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id"`
	Template     *Role         `bun:"rel:belongs-to,join:template_id=id"`
	Permissions  []Permission  `bun:"m2m:role_permissions,join:Role=Permission"`
}

Role table

func (*Role) BeforeAppendModel added in v0.0.5

func (r *Role) BeforeAppendModel(ctx context.Context, query bun.Query) error

BeforeAppendModel is called before inserting or updating a role This ensures critical validation happens at the database layer

type RolePermission

type RolePermission struct {
	bun.BaseModel `bun:"table:role_permissions,alias:rp"`

	ID           xid.ID    `bun:"id,pk,type:varchar(20)"`
	RoleID       xid.ID    `bun:"role_id,notnull,type:varchar(20)"`
	PermissionID xid.ID    `bun:"permission_id,notnull,type:varchar(20)"`
	CreatedAt    time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"`
	UpdatedAt    time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp"`

	// Relations
	Role       *Role       `bun:"rel:belongs-to,join:role_id=id"`
	Permission *Permission `bun:"rel:belongs-to,join:permission_id=id"`
}

RolePermission represents the many-to-many relationship between roles and permissions

type SSOProvider

type SSOProvider struct {
	bun.BaseModel `bun:"table:sso_providers"`

	ID        xid.ID `bun:",pk"`
	CreatedAt time.Time
	UpdatedAt time.Time

	// Multi-tenant scoping: App → Environment → Organization
	AppID          xid.ID  `bun:",notnull"`  // Platform tenant (required)
	EnvironmentID  xid.ID  `bun:",notnull"`  // Environment within app (required)
	OrganizationID *xid.ID `bun:",nullzero"` // End-user workspace (optional for app-level providers)

	ProviderID string `bun:",notnull"` // e.g., "okta-saml" or "google-oidc"
	Type       string `bun:",notnull"` // "saml" or "oidc"
	Domain     string // org domain match for auto-discovery

	// Attribute mapping from SSO assertions to user fields
	// Maps user field names to SSO attribute names
	// Example: {"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"}
	AttributeMapping map[string]string `bun:"type:jsonb"`

	// SAML configuration
	SAMLEntryPoint string // IdP SSO URL
	SAMLIssuer     string // IdP Entity ID
	SAMLCert       string // IdP signing certificate (PEM format)

	// OIDC configuration
	OIDCClientID     string // OAuth2 client ID
	OIDCClientSecret string // OAuth2 client secret
	OIDCIssuer       string // OIDC issuer URL (e.g., https://idp.example.com)
	OIDCRedirectURI  string // Callback URL for this provider
}

SSOProvider stores SSO provider configuration with multi-tenant scoping

type SecurityEvent

type SecurityEvent struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:security_events,alias:se"`

	ID        xid.ID  `bun:"id,pk,type:varchar(20)"`
	AppID     xid.ID  `bun:"app_id,notnull,type:varchar(20)"`
	UserID    *xid.ID `bun:"user_id,type:varchar(20)"`
	Type      string  `bun:"type,notnull"`
	IPAddress string  `bun:"ip_address"`
	UserAgent string  `bun:"user_agent"`
	Geo       string  `bun:"geo"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

SecurityEvent represents the security_events table

type Session

type Session struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:sessions,alias:s"`

	ID    xid.ID `bun:"id,pk,type:varchar(20)"`
	Token string `bun:"token,notnull,unique"`

	// App-centric context
	AppID          xid.ID  `bun:"app_id,notnull,type:varchar(20)"`
	EnvironmentID  *xid.ID `bun:"environment_id,type:varchar(20)"`
	OrganizationID *xid.ID `bun:"organization_id,type:varchar(20)"`

	UserID    xid.ID    `bun:"user_id,notnull,type:varchar(20)"`
	ExpiresAt time.Time `bun:"expires_at,notnull"`
	IPAddress string    `bun:"ip_address"`
	UserAgent string    `bun:"user_agent"`

	// Refresh token support (Option 3)
	RefreshToken          *string    `bun:"refresh_token,unique"`     // Long-lived refresh token
	RefreshTokenExpiresAt *time.Time `bun:"refresh_token_expires_at"` // Refresh token expiry
	LastRefreshedAt       *time.Time `bun:"last_refreshed_at"`        // When was access token last refreshed

	// Relations
	User *User `bun:"rel:belongs-to,join:user_id=id"`
}

Session represents the session table

type SocialAccount

type SocialAccount struct {
	bun.BaseModel `bun:"table:social_accounts"`

	ID        xid.ID    `bun:",pk"`
	CreatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
	UpdatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`

	// User relationship
	UserID             xid.ID  `bun:",notnull"`
	User               *User   `bun:"rel:belongs-to,join:user_id=id"`
	AppID              xid.ID  `bun:"app_id,notnull"`                        // Platform app (required)
	UserOrganizationID *xid.ID `bun:"user_organization_id,type:varchar(20)"` // User-created org (optional)

	// Provider information
	Provider   string `bun:",notnull"` // google, github, microsoft, etc.
	ProviderID string `bun:",notnull"` // Provider's unique user ID
	Email      string `bun:""`         // Email from provider (may differ from user.Email)
	Name       string `bun:""`         // Display name from provider
	Avatar     string `bun:""`         // Profile picture URL

	// OAuth tokens
	AccessToken      string     `bun:",notnull"`                  // Current access token
	RefreshToken     string     `bun:""`                          // Refresh token (if provided)
	TokenType        string     `bun:",notnull,default:'Bearer'"` // Token type
	ExpiresAt        *time.Time `bun:""`                          // Access token expiration
	RefreshExpiresAt *time.Time `bun:""`                          // Refresh token expiration
	Scope            string     `bun:""`                          // Granted scopes (comma-separated)

	// ID Token (for OIDC providers)
	IDToken string `bun:"type:text"` // Full ID token JWT

	// Provider-specific data (JSON)
	RawUserInfo string `bun:"type:jsonb"` // Raw user profile from provider

	// Account status
	Revoked   bool       `bun:",notnull,default:false"` // Whether tokens were revoked
	RevokedAt *time.Time `bun:""`                       // When account was disconnected
}

SocialAccount links a user to an OAuth provider account

func (*SocialAccount) IsRefreshTokenValid

func (sa *SocialAccount) IsRefreshTokenValid() bool

IsRefreshTokenValid checks if refresh token is valid

func (*SocialAccount) IsTokenExpired

func (sa *SocialAccount) IsTokenExpired() bool

IsTokenExpired checks if the access token has expired

func (*SocialAccount) NeedsRefresh

func (sa *SocialAccount) NeedsRefresh() bool

NeedsRefresh checks if the access token needs refreshing

type SocialProviderConfig added in v0.0.3

type SocialProviderConfig struct {
	bun.BaseModel `bun:"table:social_provider_configs,alias:spc"`

	ID        xid.ID    `bun:"id,pk,type:varchar(20)" json:"id"`
	CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"createdAt"`
	UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updatedAt"`

	// Multi-tenant scoping: App → Environment
	AppID         xid.ID `bun:"app_id,notnull,type:varchar(20)" json:"appId"`                 // Platform tenant (required)
	EnvironmentID xid.ID `bun:"environment_id,notnull,type:varchar(20)" json:"environmentId"` // Environment within app (required)

	// Provider identification
	ProviderName string `bun:"provider_name,notnull" json:"providerName"` // google, github, microsoft, apple, facebook, discord, twitter, linkedin, spotify, twitch, dropbox, gitlab, line, reddit, slack, bitbucket, notion

	// OAuth credentials
	ClientID     string `bun:"client_id,notnull" json:"clientId"`         // OAuth client ID
	ClientSecret string `bun:"client_secret,notnull" json:"-"`            // OAuth client secret (encrypted, never exposed in JSON)
	RedirectURL  string `bun:"redirect_url" json:"redirectUrl,omitempty"` // Custom redirect URL (optional, defaults to system URL)

	// OAuth scopes
	Scopes []string `bun:"scopes,type:jsonb" json:"scopes"` // OAuth scopes to request

	// Status
	IsEnabled bool `bun:"is_enabled,notnull,default:false" json:"isEnabled"` // Whether provider is active for this environment

	// Advanced provider-specific configuration
	// Examples: accessType for Google ("offline"), prompt settings, custom endpoints
	AdvancedConfig map[string]interface{} `bun:"advanced_config,type:jsonb" json:"advancedConfig,omitempty"`

	// Metadata for UI/tracking
	DisplayName string `bun:"display_name" json:"displayName,omitempty"` // Custom display name (optional)
	Description string `bun:"description" json:"description,omitempty"`  // Admin notes/description

	// Soft delete
	DeletedAt *time.Time `bun:"deleted_at,soft_delete,nullzero" json:"-"`

	// Relations
	App         *App         `bun:"rel:belongs-to,join:app_id=id" json:"app,omitempty"`
	Environment *Environment `bun:"rel:belongs-to,join:environment_id=id" json:"environment,omitempty"`
}

SocialProviderConfig stores OAuth provider configuration per app/environment This enables dashboard-based configuration of social providers instead of code-only config

func (*SocialProviderConfig) GetDisplayName added in v0.0.3

func (c *SocialProviderConfig) GetDisplayName() string

GetDisplayName returns the display name or provider name if not set

func (*SocialProviderConfig) GetEffectiveScopes added in v0.0.3

func (c *SocialProviderConfig) GetEffectiveScopes() []string

GetEffectiveScopes returns the configured scopes or defaults if empty

func (*SocialProviderConfig) HasCustomRedirectURL added in v0.0.3

func (c *SocialProviderConfig) HasCustomRedirectURL() bool

HasCustomRedirectURL returns true if a custom redirect URL is configured

func (*SocialProviderConfig) MaskClientSecret added in v0.0.3

func (c *SocialProviderConfig) MaskClientSecret() string

MaskClientSecret returns a masked version of the client secret for display

type StringArray

type StringArray []string

StringArray is a helper type for storing arrays of strings

func (*StringArray) Scan

func (s *StringArray) Scan(value interface{}) error

Scan implements sql.Scanner for StringArray

func (StringArray) Value

func (s StringArray) Value() (driver.Value, error)

Value implements driver.Valuer for StringArray

type Team

type Team struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:teams,alias:t"`

	ID          xid.ID                 `json:"id" bun:"id,pk,type:varchar(20)"`
	AppID       xid.ID                 `json:"appID" bun:"app_id,notnull,type:varchar(20)"` // App context
	Name        string                 `json:"name" bun:"name,notnull"`
	Description string                 `json:"description" bun:"description"`
	Metadata    map[string]interface{} `json:"metadata" bun:"metadata,type:jsonb"`

	// Provisioning tracking
	ProvisionedBy *string `json:"provisionedBy,omitempty" bun:"provisioned_by,type:varchar(50)"` // e.g., "scim"
	ExternalID    *string `json:"externalID,omitempty" bun:"external_id,type:varchar(255)"`      // External system ID

	// Relations
	App     *App     `json:"app,omitempty" bun:"rel:belongs-to,join:app_id=id"`
	Members []Member `json:"members,omitempty" bun:"m2m:team_members,join:Team=Member"`
}

Team represents the team table (belongs to App)

type TeamMember

type TeamMember struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:team_members,alias:tm"`

	ID       xid.ID `bun:"id,pk,type:varchar(20)"`
	TeamID   xid.ID `bun:"team_id,notnull,type:varchar(20)"`
	MemberID xid.ID `bun:"member_id,notnull,type:varchar(20)"`

	// Provisioning tracking
	ProvisionedBy *string `json:"provisionedBy,omitempty" bun:"provisioned_by,type:varchar(50)"` // e.g., "scim"

	// Relations
	Team   *Team   `bun:"rel:belongs-to,join:team_id=id"`
	Member *Member `bun:"rel:belongs-to,join:member_id=id"`
}

TeamMember represents the team_members table

type TrustedDevice

type TrustedDevice struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:trusted_devices,alias:td"`

	ID        xid.ID    `bun:"id,pk,type:varchar(20)"`
	AppID     xid.ID    `bun:"app_id,notnull,type:varchar(20)"`
	UserID    xid.ID    `bun:"user_id,notnull,type:varchar(20)"`
	DeviceID  string    `bun:"device_id,notnull"`
	ExpiresAt time.Time `bun:"expires_at,notnull"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

TrustedDevice allows skipping 2FA for a period

type TwoFASecret

type TwoFASecret struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:twofa_secrets,alias:tfs"`

	ID      xid.ID `bun:"id,pk,type:varchar(20)"`
	AppID   xid.ID `bun:"app_id,notnull,type:varchar(20)"`
	UserID  xid.ID `bun:"user_id,notnull,type:varchar(20)"`
	Method  string `bun:"method,notnull"` // totp or otp
	Secret  string `bun:"secret"`
	Enabled bool   `bun:"enabled,notnull,default:false"`

	// Relations
	App *App `bun:"rel:belongs-to,join:app_id=id"`
}

TwoFASecret stores per-user 2FA secret data

type UsageEvent

type UsageEvent struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:usage_events,alias:ue"`

	ID             xid.ID  `json:"id" bun:"id,pk,type:varchar(20)"`
	UserID         *xid.ID `json:"userId,omitempty" bun:"user_id,type:varchar(20)"`
	OrganizationID *xid.ID `json:"organizationId,omitempty" bun:"organization_id,type:varchar(20)"`
	SessionID      *xid.ID `json:"sessionId,omitempty" bun:"session_id,type:varchar(20)"`
	APIKeyID       *xid.ID `json:"apiKeyId,omitempty" bun:"api_key_id,type:varchar(20)"`

	// Request details
	Method     string `json:"method" bun:"method,notnull"`
	Path       string `json:"path" bun:"path,notnull"`
	Endpoint   string `json:"endpoint" bun:"endpoint"` // Normalized endpoint
	StatusCode int    `json:"statusCode" bun:"status_code"`

	// Authentication context
	AuthMethod string `json:"authMethod,omitempty" bun:"auth_method"` // session, apikey, jwt, anonymous

	// Network information
	IPAddress string `json:"ipAddress,omitempty" bun:"ip_address"`
	UserAgent string `json:"userAgent,omitempty" bun:"user_agent,type:text"`
	Country   string `json:"country,omitempty" bun:"country"`
	City      string `json:"city,omitempty" bun:"city"`

	// Performance metrics
	ResponseTimeMs int64 `json:"responseTimeMs" bun:"response_time_ms"`
	RequestSize    int64 `json:"requestSize" bun:"request_size"`
	ResponseSize   int64 `json:"responseSize" bun:"response_size"`

	// Feature tracking
	Plugin  string `json:"plugin,omitempty" bun:"plugin"`
	Feature string `json:"feature,omitempty" bun:"feature"`

	// Error tracking
	Error     string `json:"error,omitempty" bun:"error,type:text"`
	ErrorCode string `json:"errorCode,omitempty" bun:"error_code"`

	// Metadata
	Metadata string `json:"metadata,omitempty" bun:"metadata,type:text"` // JSON string
}

UsageEvent represents the usage_events table for tracking API usage Note: Indexes should be created in migrations for the following columns: user_id, organization_id, session_id, api_key_id, method, endpoint, status_code, auth_method, country, plugin, feature

type User

type User struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:users,alias:u"`

	ID              xid.ID     `bun:"id,pk,type:varchar(20)"`
	AppID           *xid.ID    `bun:"app_id,type:varchar(20),notnull"` // App association (required in new architecture)
	Email           string     `bun:"email,notnull"`                   // Unique per app, not globally
	EmailVerified   bool       `bun:"email_verified,notnull,default:false"`
	EmailVerifiedAt *time.Time `bun:"email_verified_at"`
	Name            string     `bun:"name"`
	Image           string     `bun:"image"`
	PasswordHash    string     `bun:"password_hash"`

	// Username support (Phase 6)
	Username        string `bun:"username,unique"`
	DisplayUsername string `bun:"display_username"`

	// Soft delete
	DeletedAt *time.Time `bun:"deleted_at"`
}

User represents the user table In the new architecture: - Users are app-scoped (can exist in multiple apps with different IDs) - Same email can exist across different apps - Unique constraint is on (app_id, email) combination - User membership to apps is managed via the Member table in app service

type UserBan

type UserBan struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:user_bans,alias:ub"`

	// Primary key
	ID xid.ID `bun:"id,pk,type:varchar(20)" json:"id"`

	// App context
	AppID xid.ID `bun:"app_id,notnull,type:varchar(20)" json:"appID"`

	// Foreign keys
	UserID       xid.ID  `bun:"user_id,notnull,type:varchar(20)" json:"userID"`
	BannedByID   xid.ID  `bun:"banned_by_id,notnull,type:varchar(20)" json:"bannedByID"`
	UnbannedByID *xid.ID `bun:"unbanned_by_id,type:varchar(20)" json:"unbannedByID,omitempty"`

	// Ban details
	Reason    string     `bun:"reason,notnull" json:"reason"`
	IsActive  bool       `bun:"is_active,notnull,default:true" json:"isActive"`
	ExpiresAt *time.Time `bun:"expires_at" json:"expiresAt,omitempty"`

	// Timestamps
	UnbannedAt *time.Time `bun:"unbanned_at" json:"unbannedAt,omitempty"`

	// Relations
	App        *App  `bun:"rel:belongs-to,join:app_id=id"`
	User       *User `bun:"rel:belongs-to,join:user_id=id" json:"user,omitempty"`
	BannedBy   *User `bun:"rel:belongs-to,join:banned_by_id=id" json:"bannedBy,omitempty"`
	UnbannedBy *User `bun:"rel:belongs-to,join:unbanned_by_id=id" json:"unbannedBy,omitempty"`
}

UserBan represents a user ban record in the database

func (*UserBan) IsCurrentlyActive

func (ub *UserBan) IsCurrentlyActive() bool

IsCurrentlyActive checks if the ban is currently active

func (*UserBan) IsExpired

func (ub *UserBan) IsExpired() bool

IsExpired checks if the ban has expired

func (UserBan) TableName

func (UserBan) TableName() string

TableName returns the table name for the UserBan model

type UserRole

type UserRole struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:user_roles,alias:ur"`

	ID     xid.ID `bun:"id,pk,type:varchar(20)"`
	UserID xid.ID `bun:"user_id,notnull,type:varchar(20)"`
	RoleID xid.ID `bun:"role_id,notnull,type:varchar(20)"`
	AppID  xid.ID `bun:"app_id,notnull,type:varchar(20)"` // App context for role assignment

	// Relations
	App  *App  `bun:"rel:belongs-to,join:app_id=id"`
	User *User `bun:"rel:belongs-to,join:user_id=id"`
	Role *Role `bun:"rel:belongs-to,join:role_id=id"`
}

UserRole maps users to roles within an app

type UserVerificationStatus

type UserVerificationStatus struct {
	bun.BaseModel `bun:"table:user_verification_status,alias:uvs"`

	ID string `bun:"id,pk,type:varchar(255)" json:"id"`

	// V2 Multi-tenant context (App → Environment → Organization)
	AppID          string  `bun:"app_id,notnull,type:varchar(20)" json:"appId"`
	EnvironmentID  *string `bun:"environment_id,type:varchar(20)" json:"environmentId,omitempty"`
	OrganizationID string  `bun:"organization_id,notnull,type:varchar(20)" json:"organizationId"`
	UserID         string  `bun:"user_id,notnull,type:varchar(20)" json:"userId"`

	// Overall verification status
	IsVerified             bool       `bun:"is_verified,default:false" json:"isVerified"`
	VerificationLevel      string     `bun:"verification_level,type:varchar(50)" json:"verificationLevel"` // none, basic, enhanced, full
	LastVerifiedAt         *time.Time `bun:"last_verified_at,type:timestamptz" json:"lastVerifiedAt,omitempty"`
	VerificationExpiry     *time.Time `bun:"verification_expiry,type:timestamptz" json:"verificationExpiry,omitempty"`
	RequiresReverification bool       `bun:"requires_reverification,default:false" json:"requiresReverification"`

	// Individual check statuses
	DocumentVerified bool `bun:"document_verified,default:false" json:"documentVerified"`
	LivenessVerified bool `bun:"liveness_verified,default:false" json:"livenessVerified"`
	AgeVerified      bool `bun:"age_verified,default:false" json:"ageVerified"`
	AMLScreened      bool `bun:"aml_screened,default:false" json:"amlScreened"`
	AMLClear         bool `bun:"aml_clear,default:false" json:"amlClear"`

	// Most recent verification IDs
	LastDocumentVerificationID string `bun:"last_document_verification_id,type:varchar(255)" json:"lastDocumentVerificationId,omitempty"`
	LastLivenessVerificationID string `bun:"last_liveness_verification_id,type:varchar(255)" json:"lastLivenessVerificationId,omitempty"`
	LastAMLVerificationID      string `bun:"last_aml_verification_id,type:varchar(255)" json:"lastAMLVerificationId,omitempty"`

	// Risk assessment
	OverallRiskLevel string   `bun:"overall_risk_level,type:varchar(20)" json:"overallRiskLevel"` // low, medium, high
	RiskFactors      []string `bun:"risk_factors,type:jsonb" json:"riskFactors,omitempty"`

	// Compliance flags
	IsBlocked   bool       `bun:"is_blocked,default:false" json:"isBlocked"`
	BlockReason string     `bun:"block_reason,type:text" json:"blockReason,omitempty"`
	BlockedAt   *time.Time `bun:"blocked_at,type:timestamptz" json:"blockedAt,omitempty"`

	// Metadata
	Metadata map[string]interface{} `bun:"metadata,type:jsonb" json:"metadata,omitempty"`

	CreatedAt time.Time `bun:"created_at,notnull,default:current_timestamp" json:"createdAt"`
	UpdatedAt time.Time `bun:"updated_at,notnull,default:current_timestamp" json:"updatedAt"`

	// Relations
	User         *User         `bun:"rel:belongs-to,join:user_id=id" json:"user,omitempty"`
	Organization *Organization `bun:"rel:belongs-to,join:organization_id=id" json:"organization,omitempty"`
}

UserVerificationStatus tracks the overall verification status of a user

type Verification

type Verification struct {
	AuditableModel `bun:",inline"`
	bun.BaseModel  `bun:"table:verifications,alias:v"`

	ID        xid.ID     `bun:"id,pk,type:varchar(20)"`
	AppID     xid.ID     `bun:"app_id,notnull,type:varchar(20)"`
	UserID    xid.ID     `bun:"user_id,notnull,type:varchar(20)"`
	Token     string     `bun:"token,notnull,unique"`
	Code      string     `bun:"code"`         // 6-digit numeric code for mobile-friendly verification
	Type      string     `bun:"type,notnull"` // email, phone, password_reset
	ExpiresAt time.Time  `bun:"expires_at,notnull"`
	Used      bool       `bun:"used,notnull,default:false"`
	UsedAt    *time.Time `bun:"used_at"`

	// Relations
	App  *App  `bun:"rel:belongs-to,join:app_id=id"`
	User *User `bun:"rel:belongs-to,join:user_id=id"`
}

Verification represents email/phone verification tokens

type Webhook

type Webhook struct {
	bun.BaseModel `bun:"table:webhooks,alias:w"`

	ID            xid.ID            `bun:"id,pk,type:varchar(20)" json:"id"`
	AppID         xid.ID            `bun:"app_id,notnull,type:varchar(20)" json:"app_id"`
	EnvironmentID xid.ID            `bun:"environment_id,notnull,type:varchar(20)" json:"environment_id"`
	URL           string            `bun:"url,notnull" json:"url"`
	Events        []string          `bun:"events,array" json:"events"`
	Secret        string            `bun:"secret,notnull" json:"-"`
	Enabled       bool              `bun:"enabled,notnull,default:true" json:"enabled"`
	MaxRetries    int               `bun:"max_retries,notnull,default:3" json:"max_retries"`
	RetryBackoff  string            `bun:"retry_backoff,notnull,default:'exponential'" json:"retry_backoff"`
	Headers       map[string]string `bun:"headers,type:jsonb" json:"headers,omitempty"`
	Metadata      map[string]string `bun:"metadata,type:jsonb" json:"metadata,omitempty"`
	LastDelivery  *time.Time        `bun:"last_delivery" json:"last_delivery,omitempty"`
	FailureCount  int               `bun:"failure_count,notnull,default:0" json:"failure_count"`
	CreatedAt     time.Time         `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"created_at"`
	UpdatedAt     time.Time         `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updated_at"`
	DeletedAt     *time.Time        `bun:"deleted_at,soft_delete,nullzero" json:"-"`
}

Webhook represents a webhook subscription

Jump to

Keyboard shortcuts

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