models

package
v1.10.0 Latest Latest
Warning

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

Go to latest
Published: Sep 13, 2026 License: AGPL-3.0 Imports: 8 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"
	DocTypeJournal      = "journal"
	DocTypeHandoff      = "handoff"
	DocTypePrompt       = "prompt"
)

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

View Source
const (
	EdgeSupersedes    = "supersedes"
	EdgeDerivedFrom   = "derived_from"
	EdgeRelatesTo     = "relates_to"
	EdgeContinuesFrom = "continues_from"
	EdgeIncludes      = "includes"
	// EdgeDependsOn: A depends_on B records that A's correctness rests on B; a
	// content change to B flags A review-pending (advisory).
	EdgeDependsOn = "depends_on"
)

Edge types name the directed, typed relationship a document asserts to another.

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 (
	MetricEventAccess  = "access"
	MetricEventVerify  = "verify"
	MetricEventCleanup = "cleanup"
)

Metric event kinds — the append-only usage events recorded per-tenant when metrics_enabled. Counters aggregate over these; stale/expired stay live COUNTs.

View Source
const (
	MutationOpCreate         = "create"
	MutationOpOverwrite      = "overwrite"
	MutationOpUpdateSection  = "update_section"
	MutationOpUpdateTitle    = "update_title"
	MutationOpDeleteSection  = "delete_section"
	MutationOpDeleteDocument = "delete_document"
)

Mutation op-type constants for MutationHistory.OpType (keep <= 32 chars).

View Source
const (
	OverrideTypeForceCreate     = "force_create"
	OverrideTypeForceRead       = "force_read"
	OverrideTypeSettingsChange  = "settings_change"
	OverrideTypeCrossTenantRead = "cross_tenant_read"
	OverrideTypePolicyChange    = "policy_change"
	OverrideTypeVerification    = "verification"
)

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"
	OverrideToolReadScope              = "read_scope"
	OverrideToolSetDocTypePolicy       = "set_doc_type_policy"
	OverrideToolMarkVerified           = "mark_verified"
)

Tool name constants for OverrideLog entries.

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

Staleness mode constants for per-tenant enforcement level. StalenessModeOff is legacy: no longer accepted, kept only for migration/normalize of stored rows.

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 ArchiveReasonSuperseded = "superseded"

ArchiveReasonSuperseded names the lifecycle rule that retired a doc via a supersedes edge (keep <= 32 chars).

View Source
const DeletionReasonRetention = "retention_sweep"

DeletionReasonRetention is the reason recorded for retention-sweep deletions.

View Source
const DocTypePoliciesNotifyChannel = "doc_type_policies_changed"

DocTypePoliciesNotifyChannel is the LISTEN/NOTIFY channel the doc_type_policies trigger signals on write; the policy store registers a reload against it.

View Source
const EmbeddingMetadataSingletonID = 1

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

View Source
const InstanceConfigNotifyChannel = "instance_config_changed"

InstanceConfigNotifyChannel is the LISTEN/NOTIFY channel the instance_config trigger signals on write; the config-invalidation listener registers a reload against it. Fixed identifier — safe to interpolate into the trigger DDL.

View Source
const InstanceConfigSingletonID = 1

InstanceConfigSingletonID is the fixed primary key of the single config row.

View Source
const ScanThreshold = 0.85

ScanThreshold gates the nightly cleanup scanner (FindNearDuplicatePairs): MAX section-pair cosine per doc pair. The write-time store_memory guard uses the per-tenant/global duplicate_threshold instead (COALESCE(override, default)).

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 DefaultDocTypePolicies = []DocTypePolicy{
	{
		DocType: DocTypeReference, VerificationAgeDays: iptr(90),
		DuplicateGuard: bptr(true), CleanupScan: bptr(true), LintStaleCheck: bptr(true),
		Embed: bptr(true), DefaultSearch: bptr(true), Prunable: bptr(false),
		WriteMode: wmptr(WriteModeReplace), SlugFormat: sfptr(SlugFormatAny), Subcategory: scptr(SubcategoryOptional),
	},
	{DocType: DocTypeProjectState, VerificationAgeDays: iptr(14)},
	{DocType: DocTypeAudit, VerificationAgeDays: iptr(30)},
	{DocType: DocTypeLearning, VerificationAgeDays: iptr(180)},
	{DocType: DocTypePreference, VerificationAgeDays: iptr(365)},
	{DocType: DocTypeTool, VerificationAgeDays: iptr(90)},
	{
		DocType: DocTypeJournal, VerificationAgeDays: iptr(0),
		DuplicateGuard: bptr(false), CleanupScan: bptr(false), LintStaleCheck: bptr(false), DefaultSearch: bptr(false),
		Prunable: bptr(true), ExpirationAgeDays: iptr(30),
		WriteMode: wmptr(WriteModeMergeSections), SlugFormat: sfptr(SlugFormatDate), Subcategory: scptr(SubcategoryForbidden),
	},
	{
		DocType: DocTypeHandoff, VerificationAgeDays: iptr(0),
		DuplicateGuard: bptr(false), CleanupScan: bptr(false), LintStaleCheck: bptr(false), DefaultSearch: bptr(false),
		Prunable: bptr(true), ExpirationAgeDays: iptr(90), Subcategory: scptr(SubcategoryRequired),
		Rules: datatypes.JSON([]byte(`{"chain_previous":{"scope":"subcategory","edge_type":"continues_from"}}`)),
	},
	{
		DocType: DocTypePrompt, VerificationAgeDays: iptr(0),
		DuplicateGuard: bptr(false), CleanupScan: bptr(false), LintStaleCheck: bptr(false),
		Prunable: bptr(false), Embed: bptr(false), DefaultSearch: bptr(false),
		WriteMode: wmptr(WriteModeReplace), Subcategory: scptr(SubcategoryRequired),
	},
}

DefaultDocTypePolicies is the seed set (spec "Seeded defaults"). reference sets every column; the rest set only what differs. NULL means inherit. Knowledge is non-prunable; journal/handoff are perishable with a fixed expiration_age_days.

View Source
var DefaultEffectivePolicies = mustResolveDefaults()

DefaultEffectivePolicies is the resolved seed set, so a store constructed but not yet Loaded (unit fixtures) still serves the seeded rules.

View Source
var KnownRuleKeys = map[string]struct{}{"chain_previous": {}}

KnownRuleKeys are the rules JSONB keys the server implements; anything else is an experimental typo lint should surface (design D1, task 9.1).

ValidDocTypes lists all accepted doc_type values.

View Source
var ValidEdgeTypes = map[string]struct{}{
	EdgeSupersedes:    {},
	EdgeDerivedFrom:   {},
	EdgeRelatesTo:     {},
	EdgeContinuesFrom: {},
	EdgeIncludes:      {},
	EdgeDependsOn:     {},
}

ValidEdgeTypes lists all accepted edge_type values (mirrors ValidDocTypes).

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{}{
	StalenessModeAdvisory: {},
	StalenessModeHard:     {},
}

ValidStalenessModes is the accepted set for Tenant.StalenessMode. advisory is the floor; off is removed and coerces via NormalizeStalenessMode.

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 NormalizeStalenessMode added in v1.9.0

func NormalizeStalenessMode(m string) string

NormalizeStalenessMode coerces a legacy or unknown mode to the advisory floor: "off", "", or any value not in ValidStalenessModes becomes advisory; a valid mode passes through unchanged.

func ParsePath

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

ParsePath splits a hierarchical path into category, subcategory, and slug. Single-part defaults to category "misc"; 4+ segments map the first to category, the last to slug, and the middle (joined by "/") to a multi-segment subcategory.

func ResolveDocTypePolicies added in v1.2.0

func ResolveDocTypePolicies(rows []DocTypePolicy) (map[string]EffectivePolicy, error)

ResolveDocTypePolicies builds the effective set: every row's NULL scalars inherit the reference row, which must exist and be fully specified. Returns a map keyed by doc_type; callers fall back to the reference entry for unknowns.

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.

func ValidateEffective added in v1.2.0

func ValidateEffective(docType string, eff EffectivePolicy) error

ValidateEffective checks a resolved policy's enums, ranges, and cross-field rules — run on the merged result so inheritance can't produce a bad combination no single row shows (design D4, spec "Rules are edited only by instance admins").

func ValidateSlugFormat added in v1.2.0

func ValidateSlugFormat(format SlugFormat, slug string) error

ValidateSlugFormat rejects (never rewrites) a slug that doesn't match the doc_type's format. InferDocType already consumed the slug to classify, so a changed slug would contradict that classification (spec "Identity validation").

func ValidateSubcategoryPath added in v1.5.0

func ValidateSubcategoryPath(subcategory string) error

ValidateSubcategoryPath validates a "/"-delimited subcategory: within the length cap, with every segment satisfying validPathSegment — so an empty segment from a leading, trailing, or doubled "/" is rejected.

func ValidateSubcategoryRule added in v1.2.0

func ValidateSubcategoryRule(rule SubcategoryRule, subcategory *string) error

ValidateSubcategoryRule enforces the doc_type's subcategory requirement.

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 ChainPrevious added in v1.2.0

type ChainPrevious struct {
	Scope    string `json:"scope"`
	EdgeType string `json:"edge_type"`
}

ChainPrevious, when present in rules, links a new document to the prior latest in its scope (as handoffs do). Scope is "subcategory"; EdgeType names the link.

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 DocTypePolicy added in v1.2.0

type DocTypePolicy struct {
	DocType             string           `gorm:"size:32;primaryKey" json:"doc_type"`
	VerificationAgeDays *int             `json:"verification_age_days"`
	ExpirationAgeDays   *int             `json:"expiration_age_days"`
	DuplicateGuard      *bool            `json:"duplicate_guard"`
	CleanupScan         *bool            `json:"cleanup_scan"`
	LintStaleCheck      *bool            `json:"lint_stale_check"`
	Embed               *bool            `json:"embed"`
	DefaultSearch       *bool            `json:"default_search"`
	Prunable            *bool            `json:"prunable"`
	WriteMode           *WriteMode       `gorm:"size:16" json:"write_mode"`
	SlugFormat          *SlugFormat      `gorm:"size:16" json:"slug_format"`
	Subcategory         *SubcategoryRule `gorm:"size:16" json:"subcategory"`
	Rules               datatypes.JSON   `gorm:"type:jsonb;not null;default:'{}'" json:"rules"`
}

DocTypePolicy is one row of doc_type_policies. Scalar rules are nullable: NULL means "inherit from the reference row", kept distinct from a set value (e.g. verification_age_days 0 = never nudge). rules holds non-scalar/experimental rules.

func (DocTypePolicy) TableName added in v1.2.0

func (DocTypePolicy) 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"`
	// ArchiveReason names the lifecycle rule that archived the doc (empty until
	// archived); carried into the deletion audit at hard-delete.
	ArchiveReason string `gorm:"size:32" json:"archive_reason,omitempty"`
	// LastAccessedAt is bumped when a search serves the doc's sections;
	// COALESCE(last_accessed_at, created_at) drives access-recency eviction (D1).
	LastAccessedAt *time.Time `json:"last_accessed_at,omitempty"`
	// Pinned exempts the doc from access-based eviction regardless of age (D4).
	Pinned bool `gorm:"not null;default:false" json:"pinned"`
	// ReviewPendingAt/ReviewReason: set when a depends_on target's content changes;
	// advisory nudge surfaced on read, cleared on re-verify. NULL = not pending.
	ReviewPendingAt *time.Time `json:"review_pending_at,omitempty"`
	ReviewReason    *string    `gorm:"size:500" json:"review_reason,omitempty"`
	// ContentHash is hex(sha256(raw markdown)); powers the write-guard exact-dup
	// short-circuit. Nullable/unbackfilled — pre-migration docs fall to the centroid.
	ContentHash string `gorm:"size:64;index" json:"-"`

	// Scope gates a document's applicability: empty = always applies, non-empty = a
	// space-separated pattern list matched against a read-time scope (drives
	// conditional includes). Allowed on any doc_type.
	Scope *string `gorm:"column:scope;size:500" json:"scope,omitempty"`

	// Display-only owning-tenant labels (not columns) — populated by the service
	// for list responses so browse shows the tenant name/type, like search does.
	TenantName string `gorm:"-" json:"tenant_name,omitempty"`
	TenantType string `gorm:"-" json:"tenant_type,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 Edge added in v1.1.1

type Edge 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"`
	SourceDocumentID uuid.UUID `gorm:"type:uuid;not null;index" json:"source_document_id"`
	TargetDocumentID uuid.UUID `gorm:"type:uuid;not null;index" json:"target_document_id"`
	EdgeType         string    `gorm:"size:32;not null" json:"edge_type"`
	ActorSubject     string    `gorm:"size:255" json:"actor_subject"`
	CreatedAt        time.Time `json:"created_at"`

	// Association fields exist only so AutoMigrate emits the OnDelete:CASCADE FKs.
	Source *Document `gorm:"foreignKey:SourceDocumentID;constraint:OnDelete:CASCADE" json:"-"`
	Target *Document `gorm:"foreignKey:TargetDocumentID;constraint:OnDelete:CASCADE" json:"-"`
}

Edge is a directed, typed doc-to-doc relationship. Both endpoints share a tenant (v1); the FK cascade drops the edge when either endpoint is hard-deleted, while archiving (which keeps the row) leaves the edge — the supersede trail.

func (Edge) TableName added in v1.1.1

func (Edge) TableName() string

type EffectivePolicy added in v1.2.0

type EffectivePolicy struct {
	VerificationAgeDays int
	ExpirationAgeDays   int
	DuplicateGuard      bool
	CleanupScan         bool
	LintStaleCheck      bool
	Embed               bool
	DefaultSearch       bool
	Prunable            bool
	WriteMode           WriteMode
	SlugFormat          SlugFormat
	Subcategory         SubcategoryRule
	ChainPrevious       *ChainPrevious
	RawRules            map[string]json.RawMessage
}

EffectivePolicy is a doc_type's rule set after NULL inheritance is resolved — the in-memory value every mechanism reads. RawRules carries the JSONB verbatim so lint can flag keys the server does not implement.

func DefaultEffectivePolicy added in v1.2.0

func DefaultEffectivePolicy() EffectivePolicy

DefaultEffectivePolicy is the reference-equivalent fallback used when no policy store is loaded (e.g. import CLI, unit fixtures) — mirrors the reference seed, so knowledge is non-prunable here too.

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 InstanceConfig added in v1.1.1

type InstanceConfig struct {
	ID uint `gorm:"primaryKey" json:"id"`

	// GlobalsSeeded guards the one-time env→DB seed of the runtime globals on an
	// existing singleton row; fresh rows are inserted already-seeded.
	GlobalsSeeded bool `gorm:"not null;default:false" json:"-"`

	// Instance toggles.
	HistoryEnabled bool `gorm:"not null;default:false" json:"history_enabled"`

	// Retrieval tuning.
	MMRLambda            float64 `gorm:"not null;default:0.5" json:"mmr_lambda"`
	StalenessPenalty     float64 `gorm:"not null;default:0.2" json:"staleness_penalty"`
	CandidatePool        int     `gorm:"not null;default:20" json:"candidate_pool"`
	SnippetChars         int     `gorm:"not null;default:400" json:"snippet_chars"`
	HistoryRetentionDays int     `gorm:"not null;default:90" json:"history_retention_days"`

	// New-tenant toggle defaults + the global near-duplicate cutoff.
	StalenessDefault      string  `gorm:"size:16;not null;default:'hard'" json:"staleness_default"`
	DuplicateGuardDefault bool    `gorm:"not null;default:true" json:"duplicate_guard_default"`
	CleanupScanDefault    bool    `gorm:"not null;default:true" json:"cleanup_scan_default"`
	DuplicateThreshold    float64 `gorm:"not null;default:0.85" json:"duplicate_threshold"`

	// Access / self-service (signup_domains + admin_emails are CSV). admin_emails
	// seeds bootstrap admins at startup; live grant/revoke is via the admin UI.
	SelfServicePolicy string `gorm:"size:16;not null;default:'open'" json:"self_service_policy"`
	SignupDomains     string `gorm:"type:text;not null;default:''" json:"signup_domains"`
	AdminEmails       string `gorm:"type:text;not null;default:''" json:"admin_emails"`

	// Maintenance.
	CleanupEnabled       bool `gorm:"not null;default:true" json:"cleanup_enabled"`
	CleanupIntervalHours int  `gorm:"not null;default:24" json:"cleanup_interval_hours"`

	// Retention sweep + metrics retention (sweep on by default; metrics 90d).
	RetentionSweepEnabled bool `gorm:"not null;default:true" json:"retention_sweep_enabled"`
	MetricsRetentionDays  int  `gorm:"not null;default:90" json:"metrics_retention_days"`

	// HTTP hardening.
	RateLimitRPS      float64 `gorm:"not null;default:20" json:"rate_limit_rps"`
	RateLimitBurst    int     `gorm:"not null;default:40" json:"rate_limit_burst"`
	TrustedProxyDepth int     `gorm:"not null;default:0" json:"trusted_proxy_depth"`
	MaxRequestBytes   int64   `gorm:"not null;default:1048576" json:"max_request_bytes"`

	// Logging + outbound webhook (empty disables).
	LogLevel   string `gorm:"size:16;not null;default:'info'" json:"log_level"`
	WebhookURL string `gorm:"type:text;not null;default:''" json:"webhook_url"`

	// RequireConfigListener fails /~/ready when the config-invalidation listener is
	// dead. Off by default: a single replica has no peers to fall behind, so a dead
	// listener is harmless there; multi-replica turns it on.
	RequireConfigListener bool `gorm:"not null;default:false" json:"require_config_listener"`

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

InstanceConfig is the singleton row holding instance-wide global settings — keyed by InstanceConfigSingletonID (the embedding_metadata singleton pattern). Env seeds these at migrate time; a stored value then wins and the admin API edits them live.

func (InstanceConfig) TableName added in v1.1.1

func (InstanceConfig) TableName() string

type InstanceConfigPatch added in v1.1.1

type InstanceConfigPatch struct {
	MMRLambda             *float64 `json:"mmr_lambda"`
	StalenessPenalty      *float64 `json:"staleness_penalty"`
	CandidatePool         *int     `json:"candidate_pool"`
	SnippetChars          *int     `json:"snippet_chars"`
	HistoryEnabled        *bool    `json:"history_enabled"`
	HistoryRetentionDays  *int     `json:"history_retention_days"`
	StalenessDefault      *string  `json:"staleness_default"`
	DuplicateGuardDefault *bool    `json:"duplicate_guard_default"`
	CleanupScanDefault    *bool    `json:"cleanup_scan_default"`
	DuplicateThreshold    *float64 `json:"duplicate_threshold"`
	SelfServicePolicy     *string  `json:"self_service_policy"`
	SignupDomains         *string  `json:"signup_domains"`
	AdminEmails           *string  `json:"admin_emails"`
	CleanupEnabled        *bool    `json:"cleanup_enabled"`
	CleanupIntervalHours  *int     `json:"cleanup_interval_hours"`
	RetentionSweepEnabled *bool    `json:"retention_sweep_enabled"`
	MetricsRetentionDays  *int     `json:"metrics_retention_days"`
	RateLimitRPS          *float64 `json:"rate_limit_rps"`
	RateLimitBurst        *int     `json:"rate_limit_burst"`
	TrustedProxyDepth     *int     `json:"trusted_proxy_depth"`
	MaxRequestBytes       *int64   `json:"max_request_bytes"`
	LogLevel              *string  `json:"log_level"`
	WebhookURL            *string  `json:"webhook_url"`
	RequireConfigListener *bool    `json:"require_config_listener"`
}

InstanceConfigPatch is a partial update of the singleton: a nil field is omitted (unchanged), a non-nil field is applied. Decoded from the admin PATCH body and consumed by InstanceConfigRepository.Update.

type MetricEvent added in v1.2.0

type MetricEvent struct {
	ID        uuid.UUID  `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	TenantID  uuid.UUID  `gorm:"type:uuid;not null" json:"tenant_id"`
	EventType string     `gorm:"size:16;not null" json:"event_type"`
	DocID     *uuid.UUID `gorm:"type:uuid" json:"doc_id,omitempty"`
	DocType   string     `gorm:"size:32" json:"doc_type,omitempty"`
	CreatedAt time.Time  `gorm:"not null;default:now()" json:"created_at"`
}

MetricEvent is an append-only per-tenant usage event (access/verify/cleanup), written best-effort off the critical path and pruned at metrics_retention_days. Indexed on (tenant_id, created_at) and (event_type, created_at) — see Migrate.

func (MetricEvent) TableName added in v1.2.0

func (MetricEvent) TableName() string

type MutationHistory added in v1.1.1

type MutationHistory struct {
	ID           uint64     `gorm:"primaryKey;autoIncrement" json:"id"`
	TenantID     uuid.UUID  `gorm:"type:uuid;not null;index" json:"tenant_id"`
	DocumentID   uuid.UUID  `gorm:"type:uuid;not null;index:idx_mutation_history_doc_time,priority:1" json:"document_id"`
	SectionID    *uuid.UUID `gorm:"type:uuid" json:"section_id,omitempty"`
	DocumentPath string     `gorm:"type:text" json:"document_path"`
	OpType       string     `gorm:"size:32;not null" json:"op_type"`
	ActorSubject string     `gorm:"size:255" json:"actor_subject"`
	ActorEmail   *string    `gorm:"type:text" json:"actor_email,omitempty"`
	APIKeyID     *uuid.UUID `gorm:"type:uuid" json:"api_key_id,omitempty"`
	Before       *string    `gorm:"type:text" json:"before,omitempty"`
	CreatedAt    time.Time  `gorm:"index:idx_mutation_history_doc_time,priority:2,sort:desc" json:"created_at"`
}

MutationHistory is an append-only audit row recording one document mutation: who changed what and — for overwrites/deletes — what it said before (JSON in Before). Written only when the global toggle is on, shared tenants only; pruned by age.

func (MutationHistory) TableName added in v1.1.1

func (MutationHistory) 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 SlugFormat added in v1.2.0

type SlugFormat string

SlugFormat constrains the slug shape a doc_type accepts on write.

const (
	SlugFormatAny      SlugFormat = "any"
	SlugFormatDate     SlugFormat = "date"
	SlugFormatDateTime SlugFormat = "datetime"
	SlugFormatKebab    SlugFormat = "kebab"
)

type SubcategoryRule added in v1.2.0

type SubcategoryRule string

SubcategoryRule requires, forbids, or allows a subcategory.

const (
	SubcategoryOptional  SubcategoryRule = "optional"
	SubcategoryRequired  SubcategoryRule = "required"
	SubcategoryForbidden SubcategoryRule = "forbidden"
)

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"`

	// 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:'advisory'" json:"staleness_mode"`
	DuplicateGuard     bool   `gorm:"not null;default:false" json:"duplicate_guard"`
	CleanupScanEnabled bool   `gorm:"not null;default:false" json:"cleanup_scan_enabled"`
	MetricsEnabled     bool   `gorm:"not null;default:false" json:"metrics_enabled"`

	// DuplicateThreshold is the per-tenant near-duplicate cutoff OVERRIDE for the
	// write guard (0<v<=1); NULL inherits the global instance_config default.
	DuplicateThreshold *float64 `gorm:"column:duplicate_threshold" json:"duplicate_threshold"`

	// 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"`
	// Identity anchor: the OIDC (issuer, subject) pair, unique together. Nullable
	// so legacy email-only rows coexist (Postgres treats NULLs as distinct) until
	// they adopt a subject on first login.
	Issuer  *string `gorm:"size:255;uniqueIndex:idx_tenant_users_iss_sub" json:"issuer,omitempty"`
	Subject *string `gorm:"size:255;uniqueIndex:idx_tenant_users_iss_sub" json:"subject,omitempty"`
	// Email is a mutable attribute kept globally unique (one email -> one identity)
	// but no longer the identity key; it refreshes from the claim on login.
	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 an OIDC identity to a tenant. Identity is anchored on the (issuer, subject) pair; the authlet AS resolves it at sign-in and auto- provisions or adopts a mapping from verified federated claims.

func (TenantUser) TableName

func (TenantUser) TableName() string

type WriteMode added in v1.2.0

type WriteMode string

WriteMode decides what happens to an existing document's sections on re-store.

const (
	WriteModeReplace       WriteMode = "replace"
	WriteModeMergeSections WriteMode = "merge_sections"
	WriteModeAppendOnly    WriteMode = "append_only"
)

Jump to

Keyboard shortcuts

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