Documentation
¶
Index ¶
- Constants
- Variables
- func GenerateSlug(name string) string
- func IsValidSlug(slug string) bool
- type AIMode
- type AISettings
- type APISettings
- type AssetIdentitySettings
- type AssetLifecycleSettings
- func (s AssetLifecycleSettings) EffectiveExcludedSourceTypes() []string
- func (s AssetLifecycleSettings) EffectiveGracePeriodDays() int
- func (s AssetLifecycleSettings) EffectiveManualReactivationGraceDays() int
- func (s AssetLifecycleSettings) EffectiveStaleThresholdDays() int
- func (s *AssetLifecycleSettings) Validate() error
- type AssetSourceSettings
- type BranchSettings
- type BrandingSettings
- type CTEMPointsConfig
- type ComponentWeights
- type ConfigOption
- type CriticalityScoreConfig
- type EmailVerificationMode
- type ExposureMultiplierConfig
- type ExposureScoreConfig
- type FindingImpactConfig
- type GeneralSettings
- type Invitation
- func (i *Invitation) Accept() error
- func (i *Invitation) AcceptedAt() *time.Time
- func (i *Invitation) CreatedAt() time.Time
- func (i *Invitation) Email() string
- func (i *Invitation) ExpiresAt() time.Time
- func (i *Invitation) ID() shared.ID
- func (i *Invitation) InvitedBy() shared.ID
- func (i *Invitation) IsAccepted() bool
- func (i *Invitation) IsExpired() bool
- func (i *Invitation) IsPending() bool
- func (i *Invitation) Role() Role
- func (i *Invitation) RoleIDs() []string
- func (i *Invitation) TenantID() shared.ID
- func (i *Invitation) Token() string
- type LLMProvider
- type MemberInfo
- type MemberSearchFilters
- type MemberSearchResult
- type MemberStats
- type MemberStatus
- type MemberWithUser
- type Membership
- func NewMembership(userID, tenantID shared.ID, role Role, invitedBy *shared.ID) (*Membership, error)
- func NewOwnerMembership(userID, tenantID shared.ID) (*Membership, error)
- func ReconstituteMembership(id shared.ID, userID shared.ID, tenantID shared.ID, role Role, ...) *Membership
- func ReconstituteMembershipWithStatus(id shared.ID, userID shared.ID, tenantID shared.ID, role Role, ...) *Membership
- func (m *Membership) CanRead() bool
- func (m *Membership) CanWrite() bool
- func (m *Membership) ID() shared.ID
- func (m *Membership) InvitedBy() *shared.ID
- func (m *Membership) IsAdmin() bool
- func (m *Membership) IsOwner() bool
- func (m *Membership) IsSuspended() bool
- func (m *Membership) JoinedAt() time.Time
- func (m *Membership) Reactivate() error
- func (m *Membership) Role() Role
- func (m *Membership) Status() MemberStatus
- func (m *Membership) Suspend(by shared.ID) error
- func (m *Membership) SuspendedAt() *time.Time
- func (m *Membership) SuspendedBy() *shared.ID
- func (m *Membership) TenantID() shared.ID
- func (m *Membership) UpdateRole(role Role) error
- func (m *Membership) UserID() shared.ID
- type PentestSettings
- type Plan
- type PlanLimits
- type Repository
- type RiskLevelConfig
- type RiskScoringSettings
- type Role
- func (r Role) CanAssignRole(target Role) bool
- func (r Role) CanDelete() bool
- func (r Role) CanInvite() bool
- func (r Role) CanManageBilling() bool
- func (r Role) CanManageMembers() bool
- func (r Role) CanRead() bool
- func (r Role) CanWrite() bool
- func (r Role) IsValid() bool
- func (r Role) Priority() int
- func (r Role) String() string
- type SecuritySettings
- type Settings
- type SeverityWeightConfig
- type Tenant
- func (t *Tenant) CreatedAt() time.Time
- func (t *Tenant) CreatedBy() string
- func (t *Tenant) Description() string
- func (t *Tenant) GetSetting(key string) (any, bool)
- func (t *Tenant) ID() shared.ID
- func (t *Tenant) LogoURL() string
- func (t *Tenant) Name() string
- func (t *Tenant) Plan() Plan
- func (t *Tenant) SetSetting(key string, value any)
- func (t *Tenant) Settings() map[string]any
- func (t *Tenant) Slug() string
- func (t *Tenant) TypedSettings() Settings
- func (t *Tenant) UpdateAISettings(ai AISettings) error
- func (t *Tenant) UpdateAPISettings(api APISettings) error
- func (t *Tenant) UpdateAssetLifecycleSettings(al AssetLifecycleSettings) error
- func (t *Tenant) UpdateAssetSourceSettings(as AssetSourceSettings) error
- func (t *Tenant) UpdateBranchSettings(bs BranchSettings) error
- func (t *Tenant) UpdateBrandingSettings(branding BrandingSettings) error
- func (t *Tenant) UpdateDescription(description string)
- func (t *Tenant) UpdateGeneralSettings(general GeneralSettings) error
- func (t *Tenant) UpdateLogoURL(logoURL string)
- func (t *Tenant) UpdateName(name string) error
- func (t *Tenant) UpdatePentestSettings(ps PentestSettings) error
- func (t *Tenant) UpdatePlan(_ Plan) error
- func (t *Tenant) UpdateRiskScoringSettings(rs RiskScoringSettings) error
- func (t *Tenant) UpdateSecuritySettings(security SecuritySettings) error
- func (t *Tenant) UpdateSettings(settings Settings) error
- func (t *Tenant) UpdateSlug(slug string) error
- func (t *Tenant) UpdatedAt() time.Time
- type TenantWithRole
- type TrustLevel
- type UserMembership
- type UserMembershipsByStatus
- type WebhookEvent
Constants ¶
const ( MinLifecycleThresholdDays = 3 MaxLifecycleThresholdDays = 365 MinGracePeriodDays = 0 MaxGracePeriodDays = 90 MinExcludedSourceTypes = 0 MaxExcludedSourceTypes = 20 )
Bounds for lifecycle threshold configuration. Mins are there to prevent operator typos from devastating the fleet (setting StaleThresholdDays=1 would mark every asset stale within hours of the next scan cycle). Maxes are a sanity ceiling — beyond 365 days lifecycle management is effectively off and the operator should just disable the feature.
const ( MaxAssetSourcePriorityLen = 500 MaxAssetSourceTrustLevels = 500 )
Upper bounds on AssetSourceSettings collections. Chosen to be comfortably above any realistic tenant size (a tenant with 500+ data sources is operating at a scale none of our customers hit today) while keeping JSON payloads bounded for DoS protection. Exceeded values return shared.ErrValidation.
const ( // DefaultInvitationExpiry is the default expiry duration for invitations. DefaultInvitationExpiry = 7 * 24 * time.Hour // 7 days )
Variables ¶
var AllRiskScoringPresets = map[string]RiskScoringSettings{ "legacy": LegacyRiskScoringSettings(), "default": DefaultRiskScoringPreset(), "banking": bankingRiskScoringPreset(), "healthcare": healthcareRiskScoringPreset(), "ecommerce": ecommerceRiskScoringPreset(), "government": governmentRiskScoringPreset(), }
AllRiskScoringPresets contains all available risk scoring presets.
var InvitableRoles = []Role{RoleAdmin, RoleMember, RoleViewer}
InvitableRoles returns the roles that can be assigned when inviting. Note: Owner role cannot be assigned via invitation.
Functions ¶
func GenerateSlug ¶
GenerateSlug generates a slug from a name.
Types ¶
type AIMode ¶
type AIMode string
AIMode represents how the tenant uses AI services.
const ( // AIModeDisabled means AI features are disabled. AIModeDisabled AIMode = "disabled" // AIModePlatform uses the platform's AI (included in subscription). AIModePlatform AIMode = "platform" // AIModeBYOK means tenant brings their own API key. AIModeBYOK AIMode = "byok" // AIModeAgent means tenant uses a self-hosted AI agent. AIModeAgent AIMode = "agent" )
type AISettings ¶
type AISettings struct {
// Mode determines how AI is used: disabled, platform, or byok
Mode AIMode `json:"mode"`
// BYOK (Bring Your Own Key) Configuration - only used when Mode = "byok"
Provider LLMProvider `json:"provider,omitempty"` // claude, openai, azure_openai
APIKey string `json:"api_key,omitempty"` // Encrypted API key (set via special endpoint)
AzureEndpoint string `json:"azure_endpoint,omitempty"` // For Azure OpenAI
ModelOverride string `json:"model_override,omitempty"` // Optional model preference
// Auto-Triage Configuration
AutoTriageEnabled bool `json:"auto_triage_enabled"` // Enable auto-triage on new findings
AutoTriageSeverities []string `json:"auto_triage_severities,omitempty"` // Severities to auto-triage: critical, high, etc.
AutoTriageDelaySeconds int `json:"auto_triage_delay_seconds"` // Delay before auto-triage (for dedup)
// Usage Limits
MonthlyTokenLimit int `json:"monthly_token_limit,omitempty"` // Optional cost control (0 = unlimited)
TokensUsedThisMonth int `json:"tokens_used_this_month"` // Tracked internally
}
AISettings contains AI/LLM configuration for the tenant.
type APISettings ¶
type APISettings struct {
APIKeyEnabled bool `json:"api_key_enabled"` // Enable API key access
WebhookURL string `json:"webhook_url"` // Webhook endpoint URL
WebhookSecret string `json:"webhook_secret"` // Webhook signing secret
WebhookEvents []WebhookEvent `json:"webhook_events"` // Events to send to webhook
}
APISettings contains API and webhook configuration.
func (*APISettings) Validate ¶
func (s *APISettings) Validate() error
Validate validates API settings.
type AssetIdentitySettings ¶ added in v0.1.7
type AssetIdentitySettings struct {
// StaleAssetDays is the number of days after which an asset is considered stale
// for IP correlation. If an existing asset hasn't been seen in this many days
// and the incoming asset has a different name, they won't be auto-merged.
// This prevents false merges due to IP reuse (DHCP).
// 0 = use system default (30 days).
StaleAssetDays int `json:"stale_asset_days,omitempty"`
// MaxIPsPerAsset limits the number of IPs stored per asset.
// Assets with more IPs than this skip IP correlation (prevents DoS).
// 0 = use system default (20).
MaxIPsPerAsset int `json:"max_ips_per_asset,omitempty"`
}
AssetIdentitySettings controls asset dedup behavior per tenant. RFC-001: Asset Identity Resolution & Deduplication.
func (AssetIdentitySettings) EffectiveMaxIPsPerAsset ¶ added in v0.1.7
func (s AssetIdentitySettings) EffectiveMaxIPsPerAsset(systemDefault int) int
EffectiveMaxIPsPerAsset returns the max IPs limit, falling back to system default.
func (AssetIdentitySettings) EffectiveStaleAssetDays ¶ added in v0.1.7
func (s AssetIdentitySettings) EffectiveStaleAssetDays(systemDefault int) int
EffectiveStaleAssetDays returns the stale threshold, falling back to system default.
type AssetLifecycleSettings ¶ added in v0.2.1
type AssetLifecycleSettings struct {
// Enabled toggles the feature. Defaults to false so tenants
// upgrading from older versions see no change. Enabling for the
// first time requires a successful dry-run (DryRunCompletedAt)
// or the force flag on the admin API.
Enabled bool `json:"enabled"`
// StaleThresholdDays — days without a MarkSeen() update before
// the worker flips status from active to stale. Default 14.
StaleThresholdDays int `json:"stale_threshold_days,omitempty"`
// GracePeriodDays — newly-discovered assets are immune from the
// lifecycle worker for this many days after `discovered_at`.
// Protects assets the scanner has not picked up yet. Default 3.
GracePeriodDays int `json:"grace_period_days,omitempty"`
// ManualReactivationGraceDays — when an operator manually
// reactivates an asset that had been flagged stale/inactive, we
// auto-set lifecycle_paused_until = NOW + this many days. Avoids
// the E2.6 flap (worker re-demotes the same asset next day).
// Default 30. Operators can override per-asset via Snooze.
ManualReactivationGraceDays int `json:"manual_reactivation_grace_days,omitempty"`
// ExcludedSourceTypes — source_type values that opt an asset
// out of the lifecycle worker. Default [manual, import]:
// user-entered data has intent behind it, the worker should
// never quietly demote a manually-curated asset.
ExcludedSourceTypes []string `json:"excluded_source_types,omitempty"`
// PauseOnIntegrationFailure — when true (default), the worker
// checks the tenant's agents and integrations before each run.
// If any are unhealthy, the whole tenant is skipped so a
// temporarily-offline scanner does not generate a false
// deactivation storm.
PauseOnIntegrationFailure bool `json:"pause_on_integration_failure,omitempty"`
// DryRunCompletedAt records the last time the tenant admin ran
// a dry-run that showed acceptable counts. Populated by the
// dry-run endpoint. A non-nil value unlocks the Enabled flag;
// unset means the API rejects Enable with "run a dry-run first".
DryRunCompletedAt *int64 `json:"dry_run_completed_at,omitempty"`
}
AssetLifecycleSettings controls automated asset status transitions based on how recently each asset has been observed by a source.
Backward compatibility: a zero-value struct (Enabled=false) disables the feature entirely — no worker run, no transitions, no UI badges. Tenants upgrading see zero behavior change until they explicitly opt in.
func DefaultAssetLifecycleSettings ¶ added in v0.2.1
func DefaultAssetLifecycleSettings() AssetLifecycleSettings
DefaultAssetLifecycleSettings returns the recommended defaults, used when a tenant has never configured lifecycle.
func (AssetLifecycleSettings) EffectiveExcludedSourceTypes ¶ added in v0.2.1
func (s AssetLifecycleSettings) EffectiveExcludedSourceTypes() []string
EffectiveExcludedSourceTypes returns the configured slice or the defaults when the field is nil/empty. Nil check is required so an upgraded tenant who never set the field gets the protective default instead of "exclude nothing".
func (AssetLifecycleSettings) EffectiveGracePeriodDays ¶ added in v0.2.1
func (s AssetLifecycleSettings) EffectiveGracePeriodDays() int
EffectiveGracePeriodDays — see EffectiveStaleThresholdDays.
func (AssetLifecycleSettings) EffectiveManualReactivationGraceDays ¶ added in v0.2.1
func (s AssetLifecycleSettings) EffectiveManualReactivationGraceDays() int
EffectiveManualReactivationGraceDays — see above.
func (AssetLifecycleSettings) EffectiveStaleThresholdDays ¶ added in v0.2.1
func (s AssetLifecycleSettings) EffectiveStaleThresholdDays() int
EffectiveStaleThresholdDays returns the configured value, falling back to the default when zero. Separate from Validate() because stored settings may have been persisted before we added a new field and we want to merge with defaults transparently.
func (*AssetLifecycleSettings) Validate ¶ added in v0.2.1
func (s *AssetLifecycleSettings) Validate() error
Validate enforces structural + range constraints. Called both from Settings.Validate (when the whole tenant settings blob is saved) and directly from the dedicated PUT /settings/asset-lifecycle endpoint.
type AssetSourceSettings ¶ added in v0.2.1
type AssetSourceSettings struct {
// SchemaVersion guards forward-incompatible changes to the
// settings payload. Bumped only when the shape changes in a
// way that legacy code can't deserialize safely.
SchemaVersion int `json:"schema_version,omitempty"`
// Priority is an ordered list of data_sources.id values —
// highest priority first. Sources not listed are ranked below
// every listed source; among unlisted sources, the existing
// last-write-wins behavior applies.
//
// Nil/empty = feature effectively off (today's behavior).
Priority []shared.ID `json:"priority,omitempty"`
// TrustLevels maps data_sources.id → TrustLevel. Expresses the
// same precedence as Priority but bucketed, matching the
// source-creation dropdown in the UI (primary/high/medium/low).
//
// When both Priority and TrustLevels are set, Priority wins for
// ordering and TrustLevels is advisory (used for the UI badge
// and for populating Priority on first save). Sources present
// in TrustLevels but missing from Priority are inserted into
// Priority at the position their bucket implies.
TrustLevels map[string]TrustLevel `json:"trust_levels,omitempty"`
// TrackFieldAttribution enables per-field source lineage —
// populates asset_sources.contributed_data with which source
// wrote which field and when. Off by default to keep storage
// cost predictable; on for tenants that want the audit trail.
TrackFieldAttribution bool `json:"track_field_attribution,omitempty"`
}
AssetSourceSettings controls how conflicting data from multiple sources is reconciled when merging into the same asset. See RFC-003 (docs/architecture/asset-source-priority.md).
Backward compatibility: a zero-value AssetSourceSettings preserves today's behavior exactly (last-write-wins at the field level). The feature only kicks in after an admin populates Priority or TrustLevels.
func (AssetSourceSettings) IsEnabled ¶ added in v0.2.1
func (s AssetSourceSettings) IsEnabled() bool
IsEnabled reports whether the tenant has configured any priority information. Callers use this to decide whether to invoke the priority gate at all — a tenant with no config skips the gate entirely and keeps today's merge behavior.
func (AssetSourceSettings) TrustLevelFor ¶ added in v0.2.1
func (s AssetSourceSettings) TrustLevelFor(sourceID shared.ID) TrustLevel
TrustLevelFor returns the configured bucket for a given source ID, or an empty level if not set. Empty is treated as "unlisted" — ranks below any configured level.
func (*AssetSourceSettings) Validate ¶ added in v0.2.1
func (s *AssetSourceSettings) Validate() error
Validate checks the settings payload for structural errors. Domain validation (UUIDs must exist + belong to the tenant) happens at the service layer where we can look up data_sources.
Error messages intentionally do NOT echo offending UUIDs back to the caller. A request that submits attacker-controlled UUIDs (e.g. probing other tenants) would otherwise receive a response that distinguishes "bad UUID format" from "unrelated UUID", and the caller already knows what they submitted. Service layer logs specifics at debug level for operator forensics.
type BranchSettings ¶
type BranchSettings struct {
// TypeRules defines custom prefix/exact-match rules for branch type detection.
// Rules are ordered; first match wins. If no rule matches, falls through
// to system defaults (feature/, release/, hotfix/, main, master, etc.).
TypeRules branch.BranchTypeRules `json:"type_rules,omitempty"`
}
BranchSettings contains branch naming convention configuration. When TypeRules is nil or empty, system defaults are used.
func (*BranchSettings) Validate ¶
func (s *BranchSettings) Validate() error
Validate validates branch settings.
type BrandingSettings ¶
type BrandingSettings struct {
PrimaryColor string `json:"primary_color"` // Hex color code, e.g., "#3B82F6"
LogoDarkURL string `json:"logo_dark_url"` // Logo for dark theme (URL)
LogoData string `json:"logo_data"` // Logo as base64 data URL (max 150KB)
}
BrandingSettings contains branding configuration.
func (*BrandingSettings) Validate ¶
func (s *BrandingSettings) Validate() error
Validate validates branding settings.
type CTEMPointsConfig ¶ added in v0.1.2
type ComponentWeights ¶ added in v0.1.2
type ConfigOption ¶ added in v0.1.2
ConfigOption represents a configurable option with value and label.
type CriticalityScoreConfig ¶ added in v0.1.2
type EmailVerificationMode ¶ added in v0.1.5
type EmailVerificationMode string
EmailVerificationMode controls per-tenant email verification behavior.
const ( // EmailVerificationAuto requires verification only when SMTP is configured. // This is the default and prevents the chicken-and-egg problem where a new // deployment can't register users because no SMTP is set up yet. EmailVerificationAuto EmailVerificationMode = "auto" // EmailVerificationAlways forces verification regardless of SMTP availability. // If SMTP is not configured, registered users will be unable to verify and // thus unable to log in — operator must configure SMTP first. EmailVerificationAlways EmailVerificationMode = "always" // EmailVerificationNever skips verification for all users in this tenant. // Use only for closed/internal deployments. Opens the door to account // hijacking via email spoofing. EmailVerificationNever EmailVerificationMode = "never" )
func (EmailVerificationMode) IsValid ¶ added in v0.1.5
func (m EmailVerificationMode) IsValid() bool
IsValid reports whether the mode is one of the allowed values.
type ExposureMultiplierConfig ¶ added in v0.1.2
type ExposureScoreConfig ¶ added in v0.1.2
type FindingImpactConfig ¶ added in v0.1.2
type FindingImpactConfig struct {
Mode string `json:"mode"`
PerFindingPoints int `json:"per_finding_points"`
FindingCap int `json:"finding_cap"`
SeverityWeights SeverityWeightConfig `json:"severity_weights"`
}
type GeneralSettings ¶
type GeneralSettings struct {
Timezone string `json:"timezone"` // e.g., "Asia/Ho_Chi_Minh", "UTC"
Language string `json:"language"` // e.g., "en", "vi"
Industry string `json:"industry"` // e.g., "technology", "finance", "healthcare"
Website string `json:"website"` // Company website URL
}
GeneralSettings contains general tenant configuration.
func (*GeneralSettings) Validate ¶
func (s *GeneralSettings) Validate() error
Validate validates general settings.
type Invitation ¶
type Invitation struct {
// contains filtered or unexported fields
}
Invitation represents an invitation to join a tenant.
func NewInvitation ¶
func NewInvitation(tenantID shared.ID, email string, role Role, invitedBy shared.ID, roleIDs []string) (*Invitation, error)
NewInvitation creates a new Invitation. invitedBy is the local user ID (from users table) of the person sending the invitation. roleIDs are the RBAC role IDs to assign when user accepts the invitation.
func ReconstituteInvitation ¶
func ReconstituteInvitation( id shared.ID, tenantID shared.ID, email string, role Role, roleIDs []string, token string, invitedBy shared.ID, expiresAt time.Time, acceptedAt *time.Time, createdAt time.Time, ) *Invitation
ReconstituteInvitation recreates an Invitation from persistence.
func (*Invitation) Accept ¶
func (i *Invitation) Accept() error
Accept marks the invitation as accepted.
func (*Invitation) AcceptedAt ¶
func (i *Invitation) AcceptedAt() *time.Time
AcceptedAt returns when the invitation was accepted (nil if not accepted).
func (*Invitation) CreatedAt ¶
func (i *Invitation) CreatedAt() time.Time
CreatedAt returns when the invitation was created.
func (*Invitation) ExpiresAt ¶
func (i *Invitation) ExpiresAt() time.Time
ExpiresAt returns when the invitation expires.
func (*Invitation) InvitedBy ¶
func (i *Invitation) InvitedBy() shared.ID
InvitedBy returns the local user ID of who sent the invitation.
func (*Invitation) IsAccepted ¶
func (i *Invitation) IsAccepted() bool
IsAccepted checks if the invitation has been accepted.
func (*Invitation) IsExpired ¶
func (i *Invitation) IsExpired() bool
IsExpired checks if the invitation has expired.
func (*Invitation) IsPending ¶
func (i *Invitation) IsPending() bool
IsPending checks if the invitation is pending (not expired and not accepted).
func (*Invitation) Role ¶
func (i *Invitation) Role() Role
Role returns the membership role to be assigned.
func (*Invitation) RoleIDs ¶
func (i *Invitation) RoleIDs() []string
RoleIDs returns the RBAC role IDs to be assigned when user accepts.
func (*Invitation) TenantID ¶
func (i *Invitation) TenantID() shared.ID
TenantID returns the tenant ID.
type LLMProvider ¶
type LLMProvider string
LLMProvider represents supported LLM providers.
const ( LLMProviderClaude LLMProvider = "claude" LLMProviderOpenAI LLMProvider = "openai" LLMProviderAzureOpenAI LLMProvider = "azure_openai" LLMProviderGemini LLMProvider = "gemini" )
func (LLMProvider) IsValid ¶
func (p LLMProvider) IsValid() bool
IsValid checks if the LLM provider is valid.
type MemberInfo ¶
type MemberInfo struct {
Membership *Membership
UserID shared.ID // Local user ID
Email string // From local users table
Name string // From local users table
AvatarURL string // From local users table
}
MemberInfo represents a membership with user info. Note: User info should be fetched from the local users table by the service layer.
type MemberSearchFilters ¶
type MemberSearchFilters struct {
Search string // Search by name or email (case-insensitive)
Limit int // Maximum number of results (0 = no limit)
Offset int // Offset for pagination
}
MemberSearchFilters defines filters for searching members.
type MemberSearchResult ¶
type MemberSearchResult struct {
Members []*MemberWithUser
Total int // Total matching members (before limit)
}
MemberSearchResult contains the search results and total count.
type MemberStats ¶
type MemberStats struct {
TotalMembers int `json:"total_members"`
ActiveMembers int `json:"active_members"`
PendingInvites int `json:"pending_invites"`
RoleCounts map[string]int `json:"role_counts"`
}
MemberStats contains statistics about tenant members.
type MemberStatus ¶ added in v0.1.5
type MemberStatus string
MemberStatus represents the lifecycle state of a membership.
const ( MemberStatusActive MemberStatus = "active" MemberStatusSuspended MemberStatus = "suspended" )
type MemberWithUser ¶
type MemberWithUser struct {
// Membership fields
ID shared.ID
UserID shared.ID
Role Role
InvitedBy *shared.ID
JoinedAt time.Time
// User fields
Email string
Name string
AvatarURL string
Status string // active, pending, inactive
LastLoginAt *time.Time
}
MemberWithUser represents a membership joined with user details.
type Membership ¶
type Membership struct {
// contains filtered or unexported fields
}
Membership represents a user's membership in a tenant. Note: Role is now stored in the user_roles table, not in tenant_members. The role field here is populated from v_user_effective_role view on read, and used for initial role assignment on create.
func NewMembership ¶
func NewMembership(userID, tenantID shared.ID, role Role, invitedBy *shared.ID) (*Membership, error)
NewMembership creates a new Membership.
func NewOwnerMembership ¶
func NewOwnerMembership(userID, tenantID shared.ID) (*Membership, error)
NewOwnerMembership creates a membership for the tenant owner.
func ReconstituteMembership ¶
func ReconstituteMembership( id shared.ID, userID shared.ID, tenantID shared.ID, role Role, invitedBy *shared.ID, joinedAt time.Time, ) *Membership
ReconstituteMembership recreates a Membership from persistence.
func ReconstituteMembershipWithStatus ¶ added in v0.1.5
func ReconstituteMembershipWithStatus( id shared.ID, userID shared.ID, tenantID shared.ID, role Role, invitedBy *shared.ID, joinedAt time.Time, status MemberStatus, suspendedAt *time.Time, suspendedBy *shared.ID, ) *Membership
ReconstituteMembershipWithStatus recreates a Membership including suspension lifecycle fields from persistence.
func (*Membership) CanRead ¶
func (m *Membership) CanRead() bool
CanRead checks if this membership has read permissions.
func (*Membership) CanWrite ¶
func (m *Membership) CanWrite() bool
CanWrite checks if this membership has write permissions.
func (*Membership) InvitedBy ¶
func (m *Membership) InvitedBy() *shared.ID
InvitedBy returns the user ID who invited this member.
func (*Membership) IsAdmin ¶
func (m *Membership) IsAdmin() bool
IsAdmin checks if this membership has admin role.
func (*Membership) IsOwner ¶
func (m *Membership) IsOwner() bool
IsOwner checks if this membership has owner role.
func (*Membership) IsSuspended ¶ added in v0.1.5
func (m *Membership) IsSuspended() bool
IsSuspended returns true if the membership is suspended.
func (*Membership) JoinedAt ¶
func (m *Membership) JoinedAt() time.Time
JoinedAt returns when the member joined.
func (*Membership) Reactivate ¶ added in v0.1.5
func (m *Membership) Reactivate() error
Reactivate marks the membership as active again. Clears the suspended_at and suspended_by fields.
func (*Membership) Status ¶ added in v0.1.5
func (m *Membership) Status() MemberStatus
Status returns the membership lifecycle state.
func (*Membership) Suspend ¶ added in v0.1.5
func (m *Membership) Suspend(by shared.ID) error
Suspend marks the membership as suspended. The caller is responsible for revoking sessions and invalidating the permission cache after calling this — the domain entity is not aware of infrastructure.
func (*Membership) SuspendedAt ¶ added in v0.1.5
func (m *Membership) SuspendedAt() *time.Time
SuspendedAt returns when the membership was suspended (nil if active).
func (*Membership) SuspendedBy ¶ added in v0.1.5
func (m *Membership) SuspendedBy() *shared.ID
SuspendedBy returns who suspended the membership (nil if active).
func (*Membership) TenantID ¶
func (m *Membership) TenantID() shared.ID
TenantID returns the tenant ID.
func (*Membership) UpdateRole ¶
func (m *Membership) UpdateRole(role Role) error
UpdateRole updates the member's role.
func (*Membership) UserID ¶
func (m *Membership) UserID() shared.ID
UserID returns the local user ID.
type PentestSettings ¶ added in v0.1.2
type PentestSettings struct {
CampaignTypes []ConfigOption `json:"campaign_types,omitempty"`
Methodologies []ConfigOption `json:"methodologies,omitempty"`
}
PentestSettings holds pentest-related configuration per tenant.
func (*PentestSettings) Validate ¶ added in v0.1.2
func (s *PentestSettings) Validate() error
Validate validates pentest settings.
type Plan ¶
type Plan string
Plan represents a tenant's module configuration. In OSS edition, all tenants have unlimited access.
const ( // PlanFree is the only plan in OSS edition - provides full access PlanFree Plan = "free" )
func (Plan) GetLimits ¶
func (p Plan) GetLimits() PlanLimits
GetLimits returns the limits for this plan. In OSS edition, all features are unlimited.
type PlanLimits ¶
type PlanLimits struct {
MaxMembers int
MaxAssets int
MaxScansMonth int
SSO bool
AuditLog bool
APIAccess bool
}
PlanLimits defines the limits for each plan. In OSS edition, all limits are unlimited (-1).
type Repository ¶
type Repository interface {
// Tenant CRUD
Create(ctx context.Context, t *Tenant) error
GetByID(ctx context.Context, id shared.ID) (*Tenant, error)
GetBySlug(ctx context.Context, slug string) (*Tenant, error)
Update(ctx context.Context, t *Tenant) error
Delete(ctx context.Context, id shared.ID) error
ExistsBySlug(ctx context.Context, slug string) (bool, error)
// ListActiveTenantIDs returns all active tenant IDs.
// Used by background jobs that need to process data across all tenants.
ListActiveTenantIDs(ctx context.Context) ([]shared.ID, error)
// Membership operations
CreateMembership(ctx context.Context, membership *Membership) error
GetMembership(ctx context.Context, userID shared.ID, tenantID shared.ID) (*Membership, error)
GetMembershipByID(ctx context.Context, id shared.ID) (*Membership, error)
UpdateMembership(ctx context.Context, membership *Membership) error
// UpdateMembershipStatus persists the lifecycle status change
// (active ↔ suspended). Used by Suspend/Reactivate service methods.
UpdateMembershipStatus(ctx context.Context, membership *Membership) error
DeleteMembership(ctx context.Context, id shared.ID) error
ListMembersByTenant(ctx context.Context, tenantID shared.ID) ([]*Membership, error)
ListMembersWithUserInfo(ctx context.Context, tenantID shared.ID) ([]*MemberWithUser, error)
SearchMembersWithUserInfo(ctx context.Context, tenantID shared.ID, filters MemberSearchFilters) (*MemberSearchResult, error)
ListTenantsByUser(ctx context.Context, userID shared.ID) ([]*TenantWithRole, error)
CountMembersByTenant(ctx context.Context, tenantID shared.ID) (int64, error)
GetMemberStats(ctx context.Context, tenantID shared.ID) (*MemberStats, error)
// GetUserMemberships returns lightweight membership data for JWT tokens.
// Suspended memberships are filtered out — they cannot mint tokens.
GetUserMemberships(ctx context.Context, userID shared.ID) ([]UserMembership, error)
// GetUserSuspendedMemberships returns the SUSPENDED memberships for a
// user. Used by the login flow so the UI can tell a suspended user
// "your access to {tenant} is suspended" instead of bouncing them to
// the create-team onboarding screen with no explanation.
GetUserSuspendedMemberships(ctx context.Context, userID shared.ID) ([]UserMembership, error)
// GetUserMembershipsWithStatus returns BOTH active and suspended
// memberships in a single query. The login flow uses this to avoid
// the two sequential round trips that GetUserMemberships +
// GetUserSuspendedMemberships would cost.
GetUserMembershipsWithStatus(ctx context.Context, userID shared.ID) (*UserMembershipsByStatus, error)
// GetMemberByEmail retrieves a member by email address within a tenant
GetMemberByEmail(ctx context.Context, tenantID shared.ID, email string) (*MemberWithUser, error)
// Invitation operations
CreateInvitation(ctx context.Context, invitation *Invitation) error
GetInvitationByToken(ctx context.Context, token string) (*Invitation, error)
GetInvitationByID(ctx context.Context, id shared.ID) (*Invitation, error)
UpdateInvitation(ctx context.Context, invitation *Invitation) error
DeleteInvitation(ctx context.Context, id shared.ID) error
ListPendingInvitationsByTenant(ctx context.Context, tenantID shared.ID) ([]*Invitation, error)
GetPendingInvitationByEmail(ctx context.Context, tenantID shared.ID, email string) (*Invitation, error)
DeleteExpiredInvitations(ctx context.Context) (int64, error)
// DeletePendingInvitationsByUserID removes every UNACCEPTED
// invitation matching the user's email in the given tenant.
// Used when removing a member to ensure they can't rejoin via
// a stale token still sitting in their inbox. Looks up the
// user's email via JOIN so the caller doesn't need to fetch it
// first. Email matching is case-insensitive. Returns the number
// of rows deleted (0 is not an error).
DeletePendingInvitationsByUserID(ctx context.Context, tenantID, userID shared.ID) (int64, error)
// AcceptInvitationTx atomically updates the invitation and creates the membership in a single transaction.
// This ensures data consistency - either both operations succeed or neither does.
AcceptInvitationTx(ctx context.Context, invitation *Invitation, membership *Membership) error
}
Repository defines the interface for tenant persistence.
type RiskLevelConfig ¶ added in v0.1.2
type RiskScoringSettings ¶ added in v0.1.2
type RiskScoringSettings struct {
Preset string `json:"preset,omitempty"`
Weights ComponentWeights `json:"weights"`
ExposureScores ExposureScoreConfig `json:"exposure_scores"`
ExposureMultipliers ExposureMultiplierConfig `json:"exposure_multipliers"`
CriticalityScores CriticalityScoreConfig `json:"criticality_scores"`
FindingImpact FindingImpactConfig `json:"finding_impact"`
CTEMPoints CTEMPointsConfig `json:"ctem_points"`
RiskLevels RiskLevelConfig `json:"risk_levels"`
}
RiskScoringSettings configures the risk scoring formula per tenant.
func DefaultRiskScoringPreset ¶ added in v0.1.2
func DefaultRiskScoringPreset() RiskScoringSettings
DefaultRiskScoringPreset returns the recommended risk scoring settings for new tenants who opt-in to configurable risk scoring.
func LegacyRiskScoringSettings ¶ added in v0.1.2
func LegacyRiskScoringSettings() RiskScoringSettings
LegacyRiskScoringSettings returns settings that reproduce the exact current hardcoded risk scoring formula for backward compatibility.
func RiskScoringPreset ¶ added in v0.1.2
func RiskScoringPreset(name string) (RiskScoringSettings, bool)
RiskScoringPreset returns a preset by name.
func (*RiskScoringSettings) Validate ¶ added in v0.1.2
func (s *RiskScoringSettings) Validate() error
Validate validates the risk scoring settings.
type Role ¶
type Role string
Role represents a user's role within a tenant.
func (Role) CanAssignRole ¶
CanAssignRole checks if this role can assign the target role to others.
func (Role) CanManageBilling ¶
CanManageBilling checks if this role can manage billing.
func (Role) CanManageMembers ¶
CanManageMembers checks if this role can manage (update/remove) members.
type SecuritySettings ¶
type SecuritySettings struct {
SSOEnabled bool `json:"sso_enabled"` // Enable SSO (SAML 2.0, OIDC)
SSOProvider string `json:"sso_provider"` // e.g., "saml", "oidc"
SSOConfigURL string `json:"sso_config_url"` // SSO metadata/config URL
MFARequired bool `json:"mfa_required"` // Require MFA for all users
SessionTimeoutMin int `json:"session_timeout_min"` // Session timeout in minutes (15-480)
IPWhitelist []string `json:"ip_whitelist"` // Allowed IP addresses/CIDR ranges
AllowedDomains []string `json:"allowed_domains"` // Allowed email domains for signup
// EmailVerificationMode controls whether new users must verify their email.
// "auto" = (default) require verification IFF SMTP is configured (smart)
// "always" = always require verification (operator must configure SMTP)
// "never" = never require verification (open registration; security risk)
EmailVerificationMode EmailVerificationMode `json:"email_verification_mode,omitempty"`
}
SecuritySettings contains security-related configuration.
func (*SecuritySettings) Validate ¶
func (s *SecuritySettings) Validate() error
Validate validates security settings.
type Settings ¶
type Settings struct {
General GeneralSettings `json:"general"`
Security SecuritySettings `json:"security"`
API APISettings `json:"api"`
Branding BrandingSettings `json:"branding"`
Branch BranchSettings `json:"branch"`
AI AISettings `json:"ai"`
RiskScoring RiskScoringSettings `json:"risk_scoring"`
Pentest PentestSettings `json:"pentest"`
AssetIdentity AssetIdentitySettings `json:"asset_identity"`
AssetSource AssetSourceSettings `json:"asset_source"`
AssetLifecycle AssetLifecycleSettings `json:"asset_lifecycle"`
}
Settings represents the typed settings for a tenant.
func DefaultSettings ¶
func DefaultSettings() Settings
DefaultSettings returns the default settings for a new tenant.
func SettingsFromMap ¶
SettingsFromMap converts map[string]any to Settings.
type SeverityWeightConfig ¶ added in v0.1.2
type Tenant ¶
type Tenant struct {
// contains filtered or unexported fields
}
Tenant represents a tenant (displayed as "Team" in UI) entity.
func Reconstitute ¶
func Reconstitute( id shared.ID, name, slug, description, logoURL string, settings map[string]any, createdBy string, createdAt, updatedAt time.Time, ) *Tenant
Reconstitute recreates a Tenant from persistence.
func (*Tenant) Description ¶
Description returns the tenant description.
func (*Tenant) GetSetting ¶
GetSetting gets a setting value.
func (*Tenant) Plan ¶
Plan returns the tenant's module configuration. In OSS edition, all tenants have the free plan with unlimited access.
func (*Tenant) SetSetting ¶
SetSetting sets a setting value.
func (*Tenant) TypedSettings ¶
TypedSettings returns the settings as a typed Settings struct.
func (*Tenant) UpdateAISettings ¶
func (t *Tenant) UpdateAISettings(ai AISettings) error
UpdateAISettings updates only the AI settings.
func (*Tenant) UpdateAPISettings ¶
func (t *Tenant) UpdateAPISettings(api APISettings) error
UpdateAPISettings updates only the API settings.
func (*Tenant) UpdateAssetLifecycleSettings ¶ added in v0.2.1
func (t *Tenant) UpdateAssetLifecycleSettings(al AssetLifecycleSettings) error
UpdateAssetLifecycleSettings updates only the asset-lifecycle settings. The DryRunCompletedAt-before-Enabled guard lives in AssetLifecycleSettings.Validate; the service layer is responsible for stamping that timestamp after a successful dry-run, not this entity method.
func (*Tenant) UpdateAssetSourceSettings ¶ added in v0.2.1
func (t *Tenant) UpdateAssetSourceSettings(as AssetSourceSettings) error
UpdateAssetSourceSettings updates only the asset-source priority configuration. RFC-003 Phase 1a. Structural validation (dup UUIDs, unknown trust levels) happens here; existence-of-source validation lives in the service layer because it needs a repository.
func (*Tenant) UpdateBranchSettings ¶
func (t *Tenant) UpdateBranchSettings(bs BranchSettings) error
UpdateBranchSettings updates only the branch naming convention settings.
func (*Tenant) UpdateBrandingSettings ¶
func (t *Tenant) UpdateBrandingSettings(branding BrandingSettings) error
UpdateBrandingSettings updates only the branding settings.
func (*Tenant) UpdateDescription ¶
UpdateDescription updates the tenant description.
func (*Tenant) UpdateGeneralSettings ¶
func (t *Tenant) UpdateGeneralSettings(general GeneralSettings) error
UpdateGeneralSettings updates only the general settings.
func (*Tenant) UpdateLogoURL ¶
UpdateLogoURL updates the tenant logo URL.
func (*Tenant) UpdateName ¶
UpdateName updates the tenant name.
func (*Tenant) UpdatePentestSettings ¶ added in v0.1.2
func (t *Tenant) UpdatePentestSettings(ps PentestSettings) error
UpdatePentestSettings updates only the pentest settings.
func (*Tenant) UpdatePlan ¶
UpdatePlan updates the tenant's module configuration. In OSS edition, this is a no-op as all tenants have unlimited access.
func (*Tenant) UpdateRiskScoringSettings ¶ added in v0.1.2
func (t *Tenant) UpdateRiskScoringSettings(rs RiskScoringSettings) error
UpdateRiskScoringSettings updates only the risk scoring settings.
func (*Tenant) UpdateSecuritySettings ¶
func (t *Tenant) UpdateSecuritySettings(security SecuritySettings) error
UpdateSecuritySettings updates only the security settings.
func (*Tenant) UpdateSettings ¶
UpdateSettings updates the tenant settings with a typed Settings struct.
func (*Tenant) UpdateSlug ¶
UpdateSlug updates the tenant slug. Note: Caller must verify uniqueness before calling this method.
type TenantWithRole ¶
TenantWithRole represents a tenant with the user's role in it.
type TrustLevel ¶ added in v0.2.1
type TrustLevel string
TrustLevel expresses how much a data source is trusted relative to others when multiple sources report conflicting values for the same asset field. Used by the ingest priority gate (RFC-003 Phase 1).
Four buckets are enough to express the common case ("Nessus always wins, manual entries always win, everything else neutral") without forcing users to reason about numeric ranks. Inside a bucket, conflicts fall back to today's behavior (last-write-wins) — see RFC-003 §Open questions Q8.
const ( // TrustLevelPrimary — authoritative source. Overrides all other // buckets. Typical use: the customer's source of truth (CMDB, // trusted scanner, manual operator entry). TrustLevelPrimary TrustLevel = "primary" // TrustLevelHigh — strongly trusted. Overrides Medium and Low. TrustLevelHigh TrustLevel = "high" // TrustLevelMedium — neutral default. Assigned at migration to // every existing source so behavior remains backward compatible // until an admin rebalances. TrustLevelMedium TrustLevel = "medium" // TrustLevelLow — least trusted. Only overrides unlisted sources // or sources with no explicit level set. TrustLevelLow TrustLevel = "low" )
func AllTrustLevels ¶ added in v0.2.1
func AllTrustLevels() []TrustLevel
AllTrustLevels returns the known levels in descending rank order. Useful for UI dropdowns and validation lists.
func DefaultTrustLevel ¶ added in v0.2.1
func DefaultTrustLevel() TrustLevel
DefaultTrustLevel returns the neutral level used when a source is created without an explicit choice. Keeps pre-feature behavior backward-compatible: every existing source starts at Medium and no tenant experiences a precedence change on upgrade.
func (TrustLevel) IsValid ¶ added in v0.2.1
func (l TrustLevel) IsValid() bool
IsValid reports whether the level is one of the four known buckets.
func (TrustLevel) Outranks ¶ added in v0.2.1
func (l TrustLevel) Outranks(other TrustLevel) bool
Outranks reports whether this level should win over other. Equal levels return false — the caller decides tie-breaking.
func (TrustLevel) Rank ¶ added in v0.2.1
func (l TrustLevel) Rank() int
Rank returns the bucket's numeric rank. Unknown or empty levels rank 0 so they always lose to any configured level.
func (TrustLevel) String ¶ added in v0.2.1
func (l TrustLevel) String() string
String implements fmt.Stringer.
func (TrustLevel) Validate ¶ added in v0.2.1
func (l TrustLevel) Validate() error
Validate returns a ValidationError when the level is unrecognized. Empty level is considered valid (means "unset") — callers who want to forbid unset levels check for the empty string themselves.
type UserMembership ¶
type UserMembership struct {
TenantID string // Tenant UUID
TenantSlug string // Tenant slug for URL-friendly access
TenantName string // Tenant display name
Role string // Role in tenant (owner, admin, member, viewer)
}
UserMembership is a lightweight struct for JWT token generation. Contains only the essential data needed for authorization.
type UserMembershipsByStatus ¶ added in v0.1.6
type UserMembershipsByStatus struct {
Active []UserMembership
Suspended []UserMembership
}
UserMembershipsByStatus partitions a user's memberships by lifecycle status. Returned by GetUserMembershipsWithStatus so the login flow can fetch both lists in a single query.
type WebhookEvent ¶
type WebhookEvent string
WebhookEvent represents a webhook event type.
const ( WebhookEventFindingCreated WebhookEvent = "finding.created" WebhookEventFindingResolved WebhookEvent = "finding.resolved" WebhookEventFindingUpdated WebhookEvent = "finding.updated" WebhookEventScanCompleted WebhookEvent = "scan.completed" WebhookEventScanFailed WebhookEvent = "scan.failed" WebhookEventAssetDiscovered WebhookEvent = "asset.discovered" WebhookEventAssetUpdated WebhookEvent = "asset.updated" WebhookEventMemberJoined WebhookEvent = "member.joined" WebhookEventMemberRemoved WebhookEvent = "member.removed" )
func ValidWebhookEvents ¶
func ValidWebhookEvents() []WebhookEvent
ValidWebhookEvents returns all valid webhook events.
func (WebhookEvent) IsValid ¶
func (e WebhookEvent) IsValid() bool
IsValid checks if the webhook event is valid.