models

package
v1.1.0 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const (
	CleanupResolutionMerged        = "merged"
	CleanupResolutionIgnored       = "ignored"
	CleanupResolutionFalsePositive = "false_positive"
)

Cleanup resolution constants.

View Source
const (
	DocTypeProjectState = "project_state"
	DocTypeAudit        = "audit"
	DocTypeLearning     = "learning"
	DocTypePreference   = "preference"
	DocTypeTool         = "tool"
	DocTypeReference    = "reference"
)

DocType enumerates document kinds for staleness threshold lookup. Agents pick one when storing; mapped automatically from category for legacy docs.

View Source
const (
	ImportJobStatusQueued    = "queued"
	ImportJobStatusRunning   = "running"
	ImportJobStatusSucceeded = "succeeded"
	ImportJobStatusFailed    = "failed"
)

Import job status values — the lifecycle a worker drives a job through: queued -> running -> (succeeded | failed).

View Source
const (
	OverrideTypeForceCreate    = "force_create"
	OverrideTypeForceRead      = "force_read"
	OverrideTypeSettingsChange = "settings_change"
)

Override type constants for OverrideLog entries.

View Source
const (
	OverrideToolStoreMemory            = "store_memory"
	OverrideToolGetDocument            = "get_document"
	OverrideToolSearchMemory           = "search_memory"
	OverrideToolUpdateSection          = "update_section"
	OverrideToolUpdateMyTenantSettings = "update_my_tenant_settings"
	OverrideToolUpdateTenantSettings   = "update_tenant_settings"
)

Tool name constants for OverrideLog entries.

View Source
const (
	DuplicateGuardThreshold = 0.70
	ScanThreshold           = 0.85
)

Duplicate-similarity thresholds. Both use section-level cosine (best-matching section pair), tuned independently:

  • DuplicateGuardThreshold gates the write-time store_memory check (FindSimilarDocuments): new section embeddings vs every existing section.
  • ScanThreshold gates the nightly cleanup scanner (FindNearDuplicatePairs): MAX cosine over all section-pair combos per doc pair.

Kept separate so a tenant can run aggressive store-time dedup without flooding the nightly queue. ScanThreshold defaults higher — scanner output is reviewer queue noise, while the write-time guard fires once per store_memory call.

View Source
const (
	StalenessModeOff      = "off"
	StalenessModeAdvisory = "advisory"
	StalenessModeHard     = "hard"
)

Staleness mode constants for per-tenant enforcement level.

View Source
const (
	TenantTypePersonal = "personal"
	TenantTypeShared   = "shared"
)

Tenant type constants. A display/visibility classifier only — see Tenant.Type.

View Source
const (
	SelfServicePolicyOpen      = "open"
	SelfServicePolicyAdminOnly = "admin_only"
)

Self-service policy constants. The optional lock over the two self-service surfaces (feature-toggle editing, API-key creation): "open" keeps today's member/owner self-service; "admin_only" raises both to admin.

View Source
const (
	TenantUserRoleMember = "member"
	TenantUserRoleAdmin  = "admin"
	TenantUserRoleOwner  = "owner"
)

Tenant user role constants at the email->tenant mapping layer. member/admin apply to any tenant; owner is personal-tenant only — a full self-manager of their own tenant (owner ⇒ manager) that is NOT a system admin.

View Source
const (
	MaxCategoryLen    = 50
	MaxSubcategoryLen = 100
	MaxSlugLen        = 100
)

Path-segment length caps, aligned with the documents table column sizes (Document: category size:50, subcategory/slug size:100). Keeping the validation limits equal to the column widths means an over-long segment is rejected as invalid input (400/errorResult) instead of surfacing as a Postgres "value too long" error (500) at write time.

View Source
const DeletionReasonRetention = "retention_sweep"

DeletionReasonRetention is the reason recorded for retention-sweep deletions.

View Source
const EmbeddingMetadataSingletonID = 1

EmbeddingMetadataSingletonID is the fixed primary key of the single metadata row.

Variables

View Source
var BootstrapTenantID = uuid.MustParse("00000000-0000-0000-0000-000000000001")

BootstrapTenantID is the well-known UUID for the default tenant. Existing data gets assigned to this tenant during migration.

View Source
var DefaultStalenessThresholds = []StalenessThreshold{
	{DocType: DocTypeProjectState, Days: 14},
	{DocType: DocTypeAudit, Days: 30},
	{DocType: DocTypeLearning, Days: 180},
	{DocType: DocTypePreference, Days: 365},
	{DocType: DocTypeTool, Days: 90},
	{DocType: DocTypeReference, Days: 90},
}

DefaultStalenessThresholds is the seed set written on first migration. Project state decays fastest; preferences essentially never.

View Source
var ValidDocTypes = map[string]struct{}{
	DocTypeProjectState: {},
	DocTypeAudit:        {},
	DocTypeLearning:     {},
	DocTypePreference:   {},
	DocTypeTool:         {},
	DocTypeReference:    {},
}

ValidDocTypes lists all accepted doc_type values.

View Source
var ValidSelfServicePolicies = map[string]struct{}{
	SelfServicePolicyOpen:      {},
	SelfServicePolicyAdminOnly: {},
}

ValidSelfServicePolicies is the accepted set for the self-service policy — both the global config default and the per-tenant override.

View Source
var ValidStalenessModes = map[string]struct{}{
	StalenessModeOff:      {},
	StalenessModeAdvisory: {},
	StalenessModeHard:     {},
}

ValidStalenessModes is the accepted set for Tenant.StalenessMode.

View Source
var ValidTenantTypes = map[string]struct{}{
	TenantTypePersonal: {},
	TenantTypeShared:   {},
}

ValidTenantTypes is the accepted set for Tenant.Type.

View Source
var ValidTenantUserRoles = map[string]struct{}{
	TenantUserRoleMember: {},
	TenantUserRoleAdmin:  {},
	TenantUserRoleOwner:  {},
}

ValidTenantUserRoles is the accepted set for TenantUser.Role.

Functions

func BuildPath

func BuildPath(category string, subcategory *string, slug string) string

BuildPath constructs a hierarchical path from category/subcategory/slug.

func InferDocType

func InferDocType(category string, subcategory *string, slug string) string

InferDocType classifies a document by category/slug when doc_type wasn't set. Mirrors the SQL backfill rules for legacy docs so new writes land the same.

func IsValidSelfServicePolicy

func IsValidSelfServicePolicy(p string) bool

IsValidSelfServicePolicy reports whether p is an accepted self-service policy.

func IsValidTenantType

func IsValidTenantType(t string) bool

IsValidTenantType reports whether t is an accepted tenant type (personal or shared).

func ParsePath

func ParsePath(path string) (category string, subcategory *string, slug string)

ParsePath splits a hierarchical path into category, subcategory, and slug. Handles 3-part / 2-part / 1-part; single-part defaults to category "misc". 4+ segments are unmappable to the category/subcategory/slug contract and return empty ("", nil, "") so the caller skips them rather than storing a mangled slash-bearing slug.

func ValidateDocumentPath

func ValidateDocumentPath(category, slug string, subcategory *string) error

ValidateDocumentPath validates a document's (category, subcategory, slug) against the shared length + character contract. subcategory nil is allowed (no subcategory); a non-nil subcategory must itself be valid. Returns a descriptive error the caller wraps (service → ErrInvalidInput, MCP → errorResult) — this package stays free of the errors/response packages.

Types

type APIKey

type APIKey struct {
	ID        uuid.UUID  `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	TenantID  uuid.UUID  `gorm:"type:uuid;not null;index" json:"tenant_id"`
	KeyHash   string     `gorm:"size:64;not null;uniqueIndex" json:"-"`
	Label     string     `gorm:"size:200;not null" json:"label"`
	Prefix    string     `gorm:"size:8;not null" json:"prefix"`
	CreatedAt time.Time  `json:"created_at"`
	RevokedAt *time.Time `json:"revoked_at,omitempty"`

	// ExpiresAt: instant after which the key stops authenticating (auth.ValidateKey).
	// NULL = never expires. Set at issue (--ttl) or by rotation's grace window.
	ExpiresAt *time.Time `json:"expires_at,omitempty"`

	// LastUsedAt: best-effort last successful validation (errors ignored). NULL =
	// never used. Admin listing uses it to spot stale keys.
	LastUsedAt *time.Time `json:"last_used_at,omitempty"`

	// SubjectID pins the key to a unified authz subject. NULL = tenant service
	// principal ("svc:<tenant_id>"); set = resolved subject id per request.
	SubjectID *string `gorm:"size:255;index" json:"subject_id,omitempty"`

	Tenant *Tenant `gorm:"foreignKey:TenantID" json:"tenant,omitempty"`
}

func (APIKey) TableName

func (APIKey) TableName() string

type CleanupQueue

type CleanupQueue struct {
	ID             uuid.UUID  `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	TenantID       uuid.UUID  `gorm:"type:uuid;not null;index:idx_cleanup_pending,priority:1" json:"tenant_id"`
	DocAID         uuid.UUID  `gorm:"type:uuid;not null" json:"doc_a_id"`
	DocBID         uuid.UUID  `gorm:"type:uuid;not null" json:"doc_b_id"`
	Similarity     float64    `gorm:"not null" json:"similarity"`
	DetectedAt     time.Time  `gorm:"not null;default:NOW()" json:"detected_at"`
	ResolvedAt     *time.Time `gorm:"index:idx_cleanup_pending,priority:2" json:"resolved_at,omitempty"`
	Resolution     *string    `gorm:"size:32" json:"resolution,omitempty"`
	ResolutionNote *string    `gorm:"type:text" json:"resolution_note,omitempty"`
	MergedInto     *uuid.UUID `gorm:"type:uuid" json:"merged_into,omitempty"`
}

CleanupQueue holds near-duplicate candidates from the nightly lint scan. A scheduled agent pulls pending rows, LLM-merges, and marks them resolved.

func (CleanupQueue) TableName

func (CleanupQueue) TableName() string

type DeletionEvent

type DeletionEvent struct {
	ID           uint64     `gorm:"primaryKey;autoIncrement" json:"id"`
	TenantID     uuid.UUID  `gorm:"type:uuid;not null;index" json:"tenant_id"`
	DocumentPath string     `gorm:"type:text;not null" json:"document_path"`
	DocType      string     `gorm:"size:32" json:"doc_type,omitempty"`
	Reason       string     `gorm:"size:32;not null" json:"reason"`
	ArchivedAt   *time.Time `json:"archived_at,omitempty"`
	DeletedAt    time.Time  `gorm:"not null;default:now()" json:"deleted_at"`
}

DeletionEvent is an append-only audit row written on hard-delete (currently only the retention sweep). Kept forever — records what was removed and when.

func (DeletionEvent) TableName

func (DeletionEvent) TableName() string

type Document

type Document struct {
	ID          uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	TenantID    uuid.UUID `gorm:"type:uuid;not null;default:'00000000-0000-0000-0000-000000000001';index" json:"tenant_id"`
	Category    string    `gorm:"size:50;not null" json:"category"`
	Subcategory *string   `gorm:"size:100" json:"subcategory,omitempty"`
	Slug        string    `gorm:"size:100;not null" json:"slug"`
	Title       string    `gorm:"size:500;not null" json:"title"`
	DocType     string    `gorm:"size:32;not null;default:'reference';index" json:"doc_type"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	// ArchivedAt marks a document retired by the retention sweep. Non-NULL =
	// excluded from all reads; hard-deleted after the delete grace period.
	ArchivedAt *time.Time `gorm:"index:idx_documents_archived_at" json:"archived_at,omitempty"`

	Tenant   *Tenant   `gorm:"foreignKey:TenantID" json:"-"`
	Sections []Section `gorm:"foreignKey:DocumentID;constraint:OnDelete:CASCADE" json:"sections,omitempty"`
}

func (Document) Path

func (d Document) Path() string

Path returns the hierarchical path like "learnings/go/gorm"

func (Document) TableName

func (Document) TableName() string

type EmbeddingMetadata

type EmbeddingMetadata struct {
	ID         uint      `gorm:"primaryKey" json:"id"`
	Provider   string    `gorm:"not null" json:"provider"`
	Model      string    `gorm:"not null" json:"model"`
	Dimensions int       `gorm:"not null" json:"dimensions"`
	UpdatedAt  time.Time `json:"updated_at"`
}

EmbeddingMetadata records the embedding identity (provider, model, dimension) that built the corpus — single row keyed by EmbeddingMetadataSingletonID. The migration guard refuses a provider/model swap on a populated corpus: it silently corrupts similarity, the duplicate guard, and retention even at the same dimension (audit #13/#16). An empty/unrecorded corpus adopts the current identity.

func (EmbeddingMetadata) TableName

func (EmbeddingMetadata) TableName() string

type ImportJob

type ImportJob struct {
	ID       uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	TenantID uuid.UUID `gorm:"type:uuid;not null;index" json:"tenant_id"`

	// Status is one of the ImportJobStatus* constants.
	Status  string `gorm:"size:20;not null;default:'queued'" json:"status"`
	Archive []byte `gorm:"type:bytea" json:"-"`

	// Progress counters, updated by the worker as it processes the archive.
	Total    int `gorm:"not null;default:0" json:"total"`
	Imported int `gorm:"not null;default:0" json:"imported"`
	Skipped  int `gorm:"not null;default:0" json:"skipped"`
	Failed   int `gorm:"not null;default:0" json:"failed"`

	// Error carries the terminal failure reason when Status == failed.
	Error string `json:"error,omitempty"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

ImportJob tracks an async document-import request: the uploaded archive (stored as bytea, bounded by config.ImportMaxUploadBytes) plus progress counters a worker updates as it extracts and ingests each document.

func (ImportJob) TableName

func (ImportJob) TableName() string

type OverrideLog

type OverrideLog struct {
	ID           uint64     `gorm:"primaryKey;autoIncrement" json:"id"`
	TenantID     uuid.UUID  `gorm:"type:uuid;not null;index:idx_override_tenant_time,priority:1" json:"tenant_id"`
	Tool         string     `gorm:"size:32;not null" json:"tool"`
	TargetID     *uuid.UUID `gorm:"type:uuid" json:"target_id,omitempty"`
	OverrideType string     `gorm:"size:32;not null" json:"override_type"`
	Reason       string     `gorm:"type:text;not null" json:"reason"`
	APIKeyID     *uuid.UUID `gorm:"type:uuid" json:"api_key_id,omitempty"`
	CreatedAt    time.Time  `gorm:"index:idx_override_tenant_time,priority:2,sort:desc" json:"created_at"`
}

OverrideLog records every force-override call against the guarded tools. Kept forever — cheap rows, valuable audit trail for detecting agent abuse.

func (OverrideLog) TableName

func (OverrideLog) TableName() string

type Section

type Section struct {
	ID         uuid.UUID       `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	DocumentID uuid.UUID       `gorm:"type:uuid;not null;index:idx_section_doc_ord" json:"document_id"`
	Ordinal    int             `gorm:"not null;index:idx_section_doc_ord" json:"ordinal"`
	Heading    *string         `gorm:"size:500" json:"heading,omitempty"`
	Content    string          `gorm:"type:text;not null" json:"content"`
	Embedding  pgvector.Vector `gorm:"type:vector" json:"-"`
	VerifiedAt *time.Time      `gorm:"index:idx_sections_verified_at" json:"verified_at,omitempty"`
	CreatedAt  time.Time       `json:"created_at"`
	UpdatedAt  time.Time       `json:"updated_at"`

	Document *Document `gorm:"foreignKey:DocumentID" json:"document,omitempty"`
}

func (Section) TableName

func (Section) TableName() string

type StalenessThreshold

type StalenessThreshold struct {
	DocType string `gorm:"size:32;primaryKey" json:"doc_type"`
	Days    int    `gorm:"not null" json:"days"`
}

StalenessThreshold maps a doc_type to its staleness threshold in days. Thresholds are configurable at runtime via the staleness_thresholds table.

func (StalenessThreshold) TableName

func (StalenessThreshold) TableName() string

type Tenant

type Tenant struct {
	ID    uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	Name  string    `gorm:"size:200;not null;uniqueIndex" json:"name"`
	Email string    `gorm:"size:200" json:"email,omitempty"`

	// Type is a DISPLAY-ONLY classifier ("personal" | "shared") for grouping
	// tenants in the UI. It MUST NOT be read by authorization: internal/authz
	// and authorize/Check never import or inspect this field, and access
	// decisions are identical regardless of its value. New tenants default to
	// "shared"; the NOT NULL DEFAULT backfills existing rows (incl. the default
	// pool) to "shared" on AutoMigrate.
	Type string `gorm:"type:text;not null;default:'shared'" json:"type"`

	// Per-tenant feature toggles. All default to the safest behavior so a tenant
	// upgrading from pre-tightening infra sees no change unless it opts in.
	StalenessMode      string `gorm:"size:16;not null;default:'off'" json:"staleness_mode"`
	DuplicateGuard     bool   `gorm:"not null;default:false" json:"duplicate_guard"`
	CleanupScanEnabled bool   `gorm:"not null;default:false" json:"cleanup_scan_enabled"`

	// SelfServicePolicy is the per-tenant override of the global self-service
	// gate: NULL = inherit the global default; else "open" | "admin_only". Set
	// and cleared by system admins only — never self-editable.
	SelfServicePolicy *string `gorm:"column:self_service_policy" json:"self_service_policy"`

	// EffectivePolicy is the resolved self-service policy (override ?? global),
	// computed on read paths — never persisted (gorm:"-").
	EffectivePolicy string `gorm:"-" json:"effective_self_service_policy,omitempty"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

func (Tenant) EffectiveSelfServicePolicy

func (t Tenant) EffectiveSelfServicePolicy(globalDefault string) string

EffectiveSelfServicePolicy resolves the tenant's effective self-service policy: the per-tenant override when set and valid, else the global default when valid, else "open" (so unset everywhere means open — today's behavior).

func (Tenant) TableName

func (Tenant) TableName() string

type TenantDefaults

type TenantDefaults struct {
	StalenessMode      string
	DuplicateGuard     bool
	CleanupScanEnabled bool
}

TenantDefaults is the operator-chosen baseline for the three per-tenant toggles. It is the single shared shape for these values across config parsing and the service create-path.

func BaselineTenantDefaults

func BaselineTenantDefaults() TenantDefaults

BaselineTenantDefaults is the built-in safe-retention bundle used when the operator sets no MEMORY_DEFAULT_OPTS override: staleness_mode=hard, duplicate_guard=true, cleanup_scan_enabled=true.

type TenantUser

type TenantUser struct {
	ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	// Email is globally unique — a single email maps to exactly one tenant.
	// size 320 = RFC 5321 max (64 local + @ + 255 domain).
	Email    string    `gorm:"size:320;not null;uniqueIndex" json:"email"`
	TenantID uuid.UUID `gorm:"type:uuid;not null;index" json:"tenant_id"`
	Role     string    `gorm:"size:16;not null;default:'member'" json:"role"`

	CreatedAt time.Time `json:"created_at"`

	// Tenant belongsTo — FK on tenant_id with ON DELETE CASCADE.
	Tenant *Tenant `gorm:"foreignKey:TenantID;constraint:OnDelete:CASCADE" json:"-"`
}

TenantUser maps a verified upstream Google email to a tenant. The authlet AS consults it at sign-in to translate an OIDC identity into a tenant_id. Rows are populated manually (admin SQL); never auto-provisioned from federated claims.

func (TenantUser) TableName

func (TenantUser) TableName() string

Jump to

Keyboard shortcuts

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