Documentation
¶
Index ¶
- Constants
- Variables
- func BuildPath(category string, subcategory *string, slug string) string
- func InferDocType(category string, subcategory *string, slug string) string
- func IsValidSelfServicePolicy(p string) bool
- func IsValidTenantType(t string) bool
- func ParsePath(path string) (category string, subcategory *string, slug string)
- func ResolveDocTypePolicies(rows []DocTypePolicy) (map[string]EffectivePolicy, error)
- func ValidateDocumentPath(category, slug string, subcategory *string) error
- func ValidateEffective(docType string, eff EffectivePolicy) error
- func ValidateSlugFormat(format SlugFormat, slug string) error
- func ValidateSubcategoryPath(subcategory string) error
- func ValidateSubcategoryRule(rule SubcategoryRule, subcategory *string) error
- type APIKey
- type ChainPrevious
- type CleanupQueue
- type DeletionEvent
- type DocTypePolicy
- type Document
- type Edge
- type EffectivePolicy
- type EmbeddingMetadata
- type ImportJob
- type InstanceConfig
- type InstanceConfigPatch
- type MetricEvent
- type MutationHistory
- type OverrideLog
- type Section
- type SlugFormat
- type SubcategoryRule
- type Tenant
- type TenantDefaults
- type TenantUser
- type WriteMode
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" DocTypePrompt = "prompt" )
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" 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.
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 ( 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.
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" OverrideTypePolicyChange = "policy_change" OverrideTypeVerification = "verification" )
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" OverrideToolSetDocTypePolicy = "set_doc_type_policy" OverrideToolMarkVerified = "mark_verified" )
Tool name constants for OverrideLog entries.
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 ArchiveReasonStale = "stale"
ArchiveReasonStale names the archive-on-grace rule: every section flagged and unverified past the doc_type's expiration_age (keep <= 32 chars).
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 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.
const EmbeddingMetadataSingletonID = 1
EmbeddingMetadataSingletonID is the fixed primary key of the single metadata row.
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.
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 DefaultDocTypePolicies = []DocTypePolicy{ { DocType: DocTypeReference, DuplicateGuard: bptr(true), CleanupScan: bptr(true), LintStaleCheck: bptr(true), Embed: bptr(true), DefaultSearch: bptr(true), Prunable: bptr(false), ExpirationAgeDays: iptr(30), WriteMode: wmptr(WriteModeReplace), SlugFormat: sfptr(SlugFormatAny), Subcategory: scptr(SubcategoryOptional), }, {DocType: DocTypeProjectState}, {DocType: DocTypeAudit}, {DocType: DocTypeLearning}, {DocType: DocTypePreference}, {DocType: DocTypeTool}, { DocType: DocTypeJournal, 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, 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, DuplicateGuard: bptr(false), CleanupScan: bptr(false), LintStaleCheck: bptr(false), Prunable: bptr(false), Embed: bptr(false), DefaultSearch: bptr(false), ExpirationAgeDays: iptr(0), 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.
var DefaultEffectivePolicies = mustResolveDefaults()
DefaultEffectivePolicies is the resolved seed set, so a store constructed but not yet Loaded (unit fixtures) still serves the seeded rules.
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).
var ValidDocTypes = map[string]struct{}{ DocTypeProjectState: {}, DocTypeAudit: {}, DocTypeLearning: {}, DocTypePreference: {}, DocTypeTool: {}, DocTypeReference: {}, DocTypeJournal: {}, DocTypeHandoff: {}, DocTypePrompt: {}, }
ValidDocTypes lists all accepted doc_type values.
var ValidEdgeTypes = map[string]struct{}{ EdgeSupersedes: {}, EdgeDerivedFrom: {}, EdgeRelatesTo: {}, EdgeContinuesFrom: {}, EdgeIncludes: {}, EdgeDependsOn: {}, }
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 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 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 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. 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 ¶
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
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"`
}
type ChainPrevious ¶ added in v1.2.0
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 for lifecycle removals: a hard-delete (retention sweep, ArchivedAt nil) or an archive-on-grace (ArchivedAt set, content preserved). Kept forever — records what was retired 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"`
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. expiration_age_days 0 = never expire). 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"`
// 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"`
}
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 EffectivePolicy ¶ added in v1.2.0
type EffectivePolicy struct {
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.
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"`
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.
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"`
CandidatePool *int `json:"candidate_pool"`
SnippetChars *int `json:"snippet_chars"`
HistoryEnabled *bool `json:"history_enabled"`
HistoryRetentionDays *int `json:"history_retention_days"`
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"`
// VerifyHints are file/symbol/line references (file:symbol, file:line) a
// git-hook matches on change via flag_changed; jsonb array, NULL/[] = none.
VerifyHints []string `gorm:"serializer:json;type:jsonb" json:"verify_hints,omitempty"`
// FlaggedAt/FlagReason: the content/event-driven needs-verification flag, set
// by verify_hints match or a depends_on change, cleared on re-verify.
FlaggedAt *time.Time `json:"flagged_at,omitempty"`
FlagReason *string `gorm:"size:500" json:"flag_reason,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Document *Document `gorm:"foreignKey:DocumentID" json:"document,omitempty"`
}
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.
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 ¶
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 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: 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