Documentation
¶
Index ¶
- Constants
- Variables
- func BuildPath(category string, subcategory *string, slug string) string
- func EpisodicDocTypes() []string
- func InferDocType(category string, subcategory *string, slug string) string
- func IsEpisodic(docType string) bool
- func IsPrunableEpisodic(docType string) bool
- func IsValidSelfServicePolicy(p string) bool
- func IsValidTenantType(t string) bool
- func ParsePath(path string) (category string, subcategory *string, slug string)
- func PrunableEpisodicDocTypes() []string
- func ValidateDocumentPath(category, slug string, subcategory *string) error
- type APIKey
- type CleanupQueue
- type DeletionEvent
- type Document
- type Edge
- type EmbeddingMetadata
- type ImportJob
- type InstanceConfig
- type InstanceConfigPatch
- type MutationHistory
- type OverrideLog
- type Section
- type StalenessThreshold
- type Tenant
- type TenantDefaults
- type TenantUser
Constants ¶
const ( CleanupResolutionMerged = "merged" CleanupResolutionIgnored = "ignored" CleanupResolutionFalsePositive = "false_positive" )
Cleanup resolution constants.
const ( DocTypeProjectState = "project_state" DocTypeAudit = "audit" DocTypeLearning = "learning" DocTypePreference = "preference" DocTypeTool = "tool" DocTypeReference = "reference" DocTypeJournal = "journal" DocTypeHandoff = "handoff" )
DocType enumerates document kinds for staleness threshold lookup. Agents pick one when storing; mapped automatically from category for legacy docs.
const ( EdgeSupersedes = "supersedes" EdgeDerivedFrom = "derived_from" EdgeRelatesTo = "relates_to" EdgeContinuesFrom = "continues_from" )
Edge types name the directed, typed relationship a document asserts to another.
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).
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).
const ( OverrideTypeForceCreate = "force_create" OverrideTypeForceRead = "force_read" OverrideTypeSettingsChange = "settings_change" OverrideTypeCrossTenantRead = "cross_tenant_read" )
Override type constants for OverrideLog entries.
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" )
Tool name constants for OverrideLog entries.
const ( StalenessModeOff = "off" StalenessModeAdvisory = "advisory" StalenessModeHard = "hard" )
Staleness mode constants for per-tenant enforcement level.
const ( TenantTypePersonal = "personal" )
Tenant type constants. A display/visibility classifier only — see Tenant.Type.
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.
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.
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.
const ArchiveReasonSuperseded = "superseded"
ArchiveReasonSuperseded names the lifecycle rule that retired a doc via a supersedes edge (keep <= 32 chars).
const DeletionReasonRetention = "retention_sweep"
DeletionReasonRetention is the reason recorded for retention-sweep deletions.
const EmbeddingMetadataSingletonID = 1
EmbeddingMetadataSingletonID is the fixed primary key of the single metadata row.
const InstanceConfigSingletonID = 1
InstanceConfigSingletonID is the fixed primary key of the single config row.
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 ¶
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.
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}, {DocType: DocTypeJournal, Days: 10}, {DocType: DocTypeHandoff, Days: 3650}, }
DefaultStalenessThresholds is the seed set written on first migration. Project state decays fastest; preferences essentially never.
var ValidDocTypes = map[string]struct{}{ DocTypeProjectState: {}, DocTypeAudit: {}, DocTypeLearning: {}, DocTypePreference: {}, DocTypeTool: {}, DocTypeReference: {}, DocTypeJournal: {}, DocTypeHandoff: {}, }
ValidDocTypes lists all accepted doc_type values.
var ValidEdgeTypes = map[string]struct{}{ EdgeSupersedes: {}, EdgeDerivedFrom: {}, EdgeRelatesTo: {}, EdgeContinuesFrom: {}, }
ValidEdgeTypes lists all accepted edge_type values (mirrors ValidDocTypes).
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.
var ValidStalenessModes = map[string]struct{}{ StalenessModeOff: {}, StalenessModeAdvisory: {}, StalenessModeHard: {}, }
ValidStalenessModes is the accepted set for Tenant.StalenessMode.
var ValidTenantTypes = map[string]struct{}{ TenantTypePersonal: {}, TenantTypeShared: {}, }
ValidTenantTypes is the accepted set for Tenant.Type.
var ValidTenantUserRoles = map[string]struct{}{ TenantUserRoleMember: {}, TenantUserRoleAdmin: {}, TenantUserRoleOwner: {}, }
ValidTenantUserRoles is the accepted set for TenantUser.Role.
Functions ¶
func EpisodicDocTypes ¶ added in v1.1.1
func EpisodicDocTypes() []string
EpisodicDocTypes returns the episodic doc_type set as a slice, for binding as a SQL array parameter (e.g. `doc_type <> ALL(?)` / `doc_type = ANY(?)`).
func InferDocType ¶
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 IsEpisodic ¶ added in v1.1.1
IsEpisodic reports whether a doc_type is episodic (see episodicDocTypes).
func IsPrunableEpisodic ¶ added in v1.1.1
IsPrunableEpisodic reports whether an episodic doc_type is classified as prunable. Never-prune types (handoff) are episodic but permanent.
func IsValidSelfServicePolicy ¶
IsValidSelfServicePolicy reports whether p is an accepted self-service policy.
func IsValidTenantType ¶
IsValidTenantType reports whether t is an accepted tenant type (personal or shared).
func ParsePath ¶
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 PrunableEpisodicDocTypes ¶ added in v1.1.1
func PrunableEpisodicDocTypes() []string
PrunableEpisodicDocTypes returns episodic doc_types classified as prunable — episodic minus the never-prune (permanent) set.
func ValidateDocumentPath ¶
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"`
}
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"`
// 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"`
// 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:"-"`
// 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"`
}
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.
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.
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"`
// 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"`
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"`
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"`
}
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 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"`
}
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"`
// 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 ¶
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).
type TenantDefaults ¶
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