domain

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: May 9, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package domain defines the core domain types for the Sercha search platform.

Caller represents request-source identity: it is constructed at every driving entry point (HTTP search handler, MCP search handler, MCP get_document handler) and threaded through SearchContext so that pipeline stages which branch on caller type — for example, to apply policy only to a specific request origin — have a stable, well-typed signal to read.

Index

Constants

View Source
const (
	ScopeMCPSearch      = "mcp:search"
	ScopeMCPDocRead     = "mcp:documents:read"
	ScopeMCPSourcesList = "mcp:sources:list"
)

MCP Scope constants

View Source
const (
	AccessTokenTTL       = 15 * time.Minute
	RefreshTokenTTL      = 30 * 24 * time.Hour // 30 days
	AuthorizationCodeTTL = 10 * time.Minute
)

Token configuration constants

Variables

View Source
var (
	// ErrNotFound indicates the requested resource was not found
	ErrNotFound = errors.New("not found")

	// ErrAlreadyExists indicates the resource already exists
	ErrAlreadyExists = errors.New("already exists")

	// ErrInvalidInput indicates the input is invalid
	ErrInvalidInput = errors.New("invalid input")

	// ErrUnauthorized indicates authentication failed or missing
	ErrUnauthorized = errors.New("unauthorized")

	// ErrForbidden indicates the user lacks permission for this action
	ErrForbidden = errors.New("forbidden")

	// ErrSyncInProgress indicates a sync is already running
	ErrSyncInProgress = errors.New("sync already in progress")

	// ErrConnectorNotFound indicates the connector type is not registered
	ErrConnectorNotFound = errors.New("connector not found")

	// ErrTokenExpired indicates the auth token has expired
	ErrTokenExpired = errors.New("token expired")

	// ErrTokenInvalid indicates the auth token is malformed or invalid
	ErrTokenInvalid = errors.New("token invalid")

	// ErrSessionNotFound indicates the session does not exist
	ErrSessionNotFound = errors.New("session not found")

	// ErrInvalidCredentials indicates wrong email/password combination
	ErrInvalidCredentials = errors.New("invalid credentials")

	// ErrInvalidProvider indicates an unknown AI provider was specified
	ErrInvalidProvider = errors.New("invalid provider")

	// ErrServiceUnavailable indicates the AI service could not be reached
	ErrServiceUnavailable = errors.New("service unavailable")

	// ErrUnsupportedProvider indicates the connector provider type is not supported
	ErrUnsupportedProvider = errors.New("unsupported provider")

	// ErrUnsupportedAuthMethod indicates the authentication method is not supported
	ErrUnsupportedAuthMethod = errors.New("unsupported auth method")

	// ErrConnectionNotFound indicates the connector connection was not found
	ErrConnectionNotFound = errors.New("connection not found")

	// ErrInUse indicates the resource is in use and cannot be deleted
	ErrInUse = errors.New("resource in use")

	// ErrInvalidClient indicates the client authentication failed
	ErrInvalidClient = errors.New("invalid client")

	// ErrInvalidGrant indicates the authorization grant is invalid, expired, or revoked
	ErrInvalidGrant = errors.New("invalid grant")

	// ErrInvalidScope indicates the requested scope is invalid or unauthorized
	ErrInvalidScope = errors.New("invalid scope")

	// ErrExpiredCode indicates the authorization code has expired
	ErrExpiredCode = errors.New("authorization code expired")

	// ErrCodeAlreadyUsed indicates the authorization code has already been used
	ErrCodeAlreadyUsed = errors.New("authorization code already used")

	// ErrInvalidRedirectURI indicates the redirect URI does not match registered URIs
	ErrInvalidRedirectURI = errors.New("invalid redirect uri")

	// ErrUnsupportedGrantType indicates the grant type is not supported
	ErrUnsupportedGrantType = errors.New("unsupported grant type")

	// ErrUnsupportedResponseType indicates the response type is not supported
	ErrUnsupportedResponseType = errors.New("unsupported response type")

	// ErrInvalidCodeChallenge indicates the PKCE code challenge is invalid
	ErrInvalidCodeChallenge = errors.New("invalid code challenge")

	// ErrTokenRevoked indicates the token has been revoked
	ErrTokenRevoked = errors.New("token revoked")

	// ErrRateLimitExceeded indicates the LLM service rate limit was exceeded
	ErrRateLimitExceeded = errors.New("rate limit exceeded")

	// ErrContextLengthExceeded indicates the input exceeds the LLM's context window
	ErrContextLengthExceeded = errors.New("context length exceeded")

	// ErrInvalidModel indicates the specified model is not available or supported
	ErrInvalidModel = errors.New("invalid model")
)

Domain errors - used across all layers

DefaultMCPScopes contains the default scopes for MCP clients

Functions

func GenerateID

func GenerateID() string

GenerateID creates a unique random ID.

func MatchesMimePattern added in v0.2.1

func MatchesMimePattern(mimeType string, pattern string) bool

MatchesMimePattern checks if a MIME type matches a pattern. Supports both exact matches (e.g., "image/png") and wildcard matches (e.g., "image/*"). The pattern is case-insensitive.

Examples:

  • MatchesMimePattern("image/png", "image/*") returns true
  • MatchesMimePattern("image/png", "image/png") returns true
  • MatchesMimePattern("image/png", "text/*") returns false
  • MatchesMimePattern("text/plain", "text/plain") returns true
  • MatchesMimePattern("application/json", "application/*") returns true

func PlatformDisplayName added in v0.2.1

func PlatformDisplayName(platform PlatformType) string

PlatformDisplayName returns a human-readable name for a platform. Custom names registered via RegisterPlatformDisplayName take precedence; otherwise the built-in switch is consulted; otherwise the raw PlatformType string is returned.

func RegisterPlatformDisplayName added in v0.4.0

func RegisterPlatformDisplayName(platform PlatformType, displayName string)

RegisterPlatformDisplayName declares a human-readable display name for a PlatformType not represented in the built-in switch. Optional companion to RegisterPlatformMapping for entirely new platforms.

func RegisterPlatformMapping added in v0.4.0

func RegisterPlatformMapping(provider ProviderType, platform PlatformType)

RegisterPlatformMapping declares that a custom ProviderType belongs to an existing PlatformType. Wiring code (typically the binary's main) calls this at startup before any sync work begins. Subsequent calls to PlatformFor and ServicesFor honour the registration.

The corresponding PlatformDisplayName falls back to the platform's built-in display name. Use RegisterPlatformDisplayName to override it for a brand-new PlatformType not represented in the built-in switch.

Safe to call concurrently with itself; not designed to race with reads.

func ShouldExcludeMimeType added in v0.2.1

func ShouldExcludeMimeType(mimeType string, exclusionPatterns []string) bool

ShouldExcludeMimeType checks if a MIME type should be excluded based on a list of patterns. Returns true if the MIME type matches any of the exclusion patterns.

Types

type AIModelInfo

type AIModelInfo struct {
	ID         string `json:"id"`                   // Model identifier (e.g., "text-embedding-3-small")
	Name       string `json:"name"`                 // Display name
	Dimensions int    `json:"dimensions,omitempty"` // For embedding models only
}

AIModelInfo describes a specific model offered by a provider

type AIProvider

type AIProvider string

AIProvider identifies the AI/embedding provider

const (
	AIProviderOpenAI    AIProvider = "openai"
	AIProviderAnthropic AIProvider = "anthropic"
	AIProviderOllama    AIProvider = "ollama"
	AIProviderCohere    AIProvider = "cohere"
	AIProviderVoyage    AIProvider = "voyage"
)

func (AIProvider) IsValid

func (p AIProvider) IsValid() bool

IsValid returns true if this is a known provider

func (AIProvider) RequiresAPIKey

func (p AIProvider) RequiresAPIKey() bool

RequiresAPIKey returns true if this provider requires an API key

type AIProviderInfo

type AIProviderInfo struct {
	ID              string        `json:"id"`                    // Provider identifier (matches AIProvider)
	Name            string        `json:"name"`                  // Display name
	Models          []AIModelInfo `json:"models"`                // Available models
	RequiresAPIKey  bool          `json:"requires_api_key"`      // Whether API key is needed
	RequiresBaseURL bool          `json:"requires_base_url"`     // Whether base URL is needed (e.g., Ollama)
	APIKeyURL       string        `json:"api_key_url,omitempty"` // URL to get API key
}

AIProviderInfo provides metadata about an AI provider

type AISettings

type AISettings struct {
	TeamID    string            `json:"team_id"`
	Embedding EmbeddingSettings `json:"embedding"`
	LLM       LLMSettings       `json:"llm"`
	UpdatedAt time.Time         `json:"updated_at"`
}

AISettings holds AI service configuration (embedding and LLM) This can be updated at runtime via API

func (*AISettings) Validate

func (s *AISettings) Validate() error

Validate checks if AISettings are valid

type AnalyticsPeriod

type AnalyticsPeriod struct {
	Start time.Time `json:"start"`
	End   time.Time `json:"end"`
}

AnalyticsPeriod represents the time range for analytics

func Last7Days

func Last7Days() AnalyticsPeriod

Last7Days creates a period for the last 7 days

func Last24Hours

func Last24Hours() AnalyticsPeriod

Last24Hours creates a period for the last 24 hours

func Last30Days

func Last30Days() AnalyticsPeriod

Last30Days creates a period for the last 30 days

func NewAnalyticsPeriod

func NewAnalyticsPeriod(start, end time.Time) AnalyticsPeriod

NewAnalyticsPeriod creates a new analytics period

type AuthContext

type AuthContext struct {
	UserID    string `json:"user_id"`
	Email     string `json:"email"`
	Name      string `json:"name"`
	Role      Role   `json:"role"`
	TeamID    string `json:"team_id"`
	SessionID string `json:"session_id"`
}

AuthContext contains authenticated user info for request context

func (*AuthContext) IsAdmin

func (a *AuthContext) IsAdmin() bool

IsAdmin checks if the authenticated user is an admin

type AuthMethod

type AuthMethod string

AuthMethod defines how to authenticate with a provider

const (
	AuthMethodOAuth2         AuthMethod = "oauth2"
	AuthMethodAPIKey         AuthMethod = "api_key"
	AuthMethodBasic          AuthMethod = "basic"
	AuthMethodServiceAccount AuthMethod = "service_account"
	AuthMethodPAT            AuthMethod = "pat" // Personal Access Token

	// AuthMethodAppOnly represents the client_credentials (app-only) OAuth 2.0
	// flow. Unlike AuthMethodOAuth2, which is a delegated grant tied to a
	// specific signed-in user, AuthMethodAppOnly uses a long-lived application
	// credential (client secret or certificate) to obtain access tokens on behalf
	// of the application itself — no user interaction required.
	//
	// The application credential is referenced by AppCredentialsRef on the
	// Connection and is never stored directly in the Connection record. The
	// identity scope (tenant, org, customer) the credential is valid for is
	// captured in TenantID. The format of both fields is provider-defined.
	AuthMethodAppOnly AuthMethod = "app_only"
)

type AuthProvider

type AuthProvider struct {
	Type         ProviderType `json:"type"`
	Name         string       `json:"name"`         // Display name
	AuthURL      string       `json:"auth_url"`     // OAuth authorization URL
	TokenURL     string       `json:"token_url"`    // OAuth token URL
	Scopes       []string     `json:"scopes"`       // Required OAuth scopes
	ClientID     string       `json:"client_id"`    // OAuth client ID (public)
	ClientSecret string       `json:"-"`            // OAuth client secret (never serialize)
	RedirectURL  string       `json:"redirect_url"` // OAuth callback URL
}

AuthProvider holds OAuth configuration for a provider

type AuthorizationCode added in v0.2.1

type AuthorizationCode struct {
	Code          string    `json:"code"`
	ClientID      string    `json:"client_id"`
	UserID        string    `json:"user_id"`
	RedirectURI   string    `json:"redirect_uri"`
	Scopes        []string  `json:"scopes"`
	CodeChallenge string    `json:"code_challenge"` // PKCE S256 challenge
	Resource      string    `json:"resource"`       // RFC 8707 resource indicator (audience)
	ExpiresAt     time.Time `json:"expires_at"`     // 10 minutes
	Used          bool      `json:"used"`           // single-use flag
	CreatedAt     time.Time `json:"created_at"`
}

AuthorizationCode represents a short-lived authorization code for the auth code flow

func (*AuthorizationCode) IsExpired added in v0.2.1

func (a *AuthorizationCode) IsExpired() bool

IsExpired checks if the authorization code has expired

func (*AuthorizationCode) IsUsable added in v0.2.1

func (a *AuthorizationCode) IsUsable() bool

IsUsable checks if the authorization code can be used (not expired and not already used)

type AuthorizeRequest added in v0.2.1

type AuthorizeRequest struct {
	ResponseType        string `json:"response_type"` // "code"
	ClientID            string `json:"client_id"`
	RedirectURI         string `json:"redirect_uri"`
	Scope               string `json:"scope"`                 // Space-delimited scopes
	State               string `json:"state,omitempty"`       // CSRF token
	CodeChallenge       string `json:"code_challenge"`        // PKCE challenge
	CodeChallengeMethod string `json:"code_challenge_method"` // "S256"
	Resource            string `json:"resource,omitempty"`    // RFC 8707 resource indicator
}

AuthorizeRequest represents an OAuth 2.0 authorization request

func (*AuthorizeRequest) ParseScopes added in v0.2.1

func (r *AuthorizeRequest) ParseScopes() []string

ParseScopes converts space-delimited scope string to array

type AvailabilityFunc added in v0.4.0

type AvailabilityFunc func(ctx context.Context, t CapabilityType) bool

AvailabilityFunc is a function adapter for AvailabilityResolver.

func (AvailabilityFunc) IsAvailable added in v0.4.0

func (f AvailabilityFunc) IsAvailable(ctx context.Context, t CapabilityType) bool

IsAvailable implements AvailabilityResolver.

type AvailabilityResolver added in v0.4.0

type AvailabilityResolver interface {
	// IsAvailable returns true when the runtime can support this
	// capability now. Returning false for a capability that has been
	// registered is normal — it just means the backend isn't configured
	// or healthy at this moment.
	//
	// For an unknown capability type (one not registered) the resolver
	// SHOULD return false to surface the configuration mistake.
	IsAvailable(ctx context.Context, t CapabilityType) bool
}

AvailabilityResolver answers whether a capability is currently available in the running system. Composable: a base resolver covers built-in backend checks (search engine, vector store, LLM) and consumers can wrap it to add new rules for their own capability types.

Resolvers must be safe for concurrent use. They are called from request handling paths and should not perform blocking I/O — runtime services usually expose simple availability getters that the resolver consults.

type Caller added in v0.4.0

type Caller struct {
	// Source identifies the entry point that constructed this Caller.
	Source CallerSource `json:"source"`

	// UserID is the authenticated user performing the request.
	// Empty string signals an unauthenticated or service-to-service call.
	UserID string `json:"user_id"`
}

Caller carries the request-source identity for a single pipeline execution.

This is the base form understood by Core. Consumers may extend it with additional metadata (e.g. OAuth2 client identity, delegation mode) by embedding this struct in a richer type and threading the richer value through the request context — see the CallerEnricher hook on the MCP adapter for the canonical pattern.

The UserID field mirrors SearchContext.UserID — it is duplicated here so that stages receiving only the Caller have all the identity they need without an extra context lookup.

type CallerSource added in v0.4.0

type CallerSource string

CallerSource identifies the entry point through which a search request arrived. It is an open string type following the same convention as SearchMode — a small fixed set of constants covers the currently known values, but consumers should not panic on unrecognised values.

const (
	// CallerSourceDirect indicates the request arrived via the direct HTTP search
	// API (the UI or REST API consumer).
	CallerSourceDirect CallerSource = "direct"

	// CallerSourceMCP indicates the request arrived from an OAuth2 LLM client via
	// the Model Context Protocol server.
	CallerSourceMCP CallerSource = "mcp"
)

type Capability

type Capability struct {
	Type        CapabilityType   `json:"type"`
	DisplayName string           `json:"display_name"`
	Description string           `json:"description"`
	Phase       PipelinePhase    `json:"phase"`
	BackendID   string           `json:"backend_id,omitempty"`
	DependsOn   []CapabilityType `json:"depends_on,omitempty"`
	Grants      []CapabilityType `json:"grants,omitempty"`

	// Available means the runtime can support this capability (backends are
	// configured, services are healthy, etc.). Computed by the resolver.
	Available bool `json:"available"`

	// Enabled means the operator has turned this capability on. Reflects
	// per-team preference, falling back to the descriptor's DefaultEnabled.
	Enabled bool `json:"enabled"`
}

Capability is the resolved runtime state of a single capability for a particular team. Built by combining a CapabilityDescriptor with availability and persisted preferences.

func ResolveCapabilities

func ResolveCapabilities(
	descriptors []CapabilityDescriptor,
	available map[CapabilityType]bool,
	prefs *CapabilityPreferences,
) []*Capability

ResolveCapabilities builds the runtime list of resolved Capability values from descriptors + availability + persisted preferences. Dependency cascades are enforced: a capability whose dependency is not active is marked disabled.

The function does not interpret any specific capability type — it walks descriptors generically. Adding a new capability requires only registering a descriptor (and an availability rule); no code change to this function is needed.

func (*Capability) IsActive

func (c *Capability) IsActive() bool

IsActive reports whether the capability is both available and enabled — i.e. ready to use right now.

func (*Capability) IsIndexingCapability

func (c *Capability) IsIndexingCapability() bool

IsIndexingCapability reports whether this is an indexing-phase capability.

func (*Capability) IsSearchCapability

func (c *Capability) IsSearchCapability() bool

IsSearchCapability reports whether this is a search-phase capability.

type CapabilityDescriptor added in v0.4.0

type CapabilityDescriptor struct {
	// Type is the unique string key. Must match the registry's view.
	Type CapabilityType `json:"type"`

	// DisplayName is the human-readable label rendered in the admin UI.
	DisplayName string `json:"display_name"`

	// Description is one-line guidance text rendered alongside the toggle.
	Description string `json:"description"`

	// Phase indicates whether this is an indexing or search capability.
	Phase PipelinePhase `json:"phase"`

	// BackendID is an optional tag identifying the implementation backend
	// (e.g. "opensearch", "pgvector", "llm"). Used purely for UX
	// (grouping, badges); the capability resolution path does not consult
	// it. Empty when the capability has no specific backend.
	BackendID string `json:"backend_id,omitempty"`

	// DependsOn lists other capability types that must be enabled and
	// available for this capability to function. The resolver cascades
	// disable: if any dependency is not active, this capability is
	// marked disabled.
	DependsOn []CapabilityType `json:"depends_on,omitempty"`

	// Grants lists capabilities that this capability enables. Reverse of
	// DependsOn — purely informational for the UI today.
	Grants []CapabilityType `json:"grants,omitempty"`

	// DefaultEnabled is the toggle's default value when no per-team
	// preference exists. The resolver uses this as the initial enabled
	// state before applying stored preferences.
	DefaultEnabled bool `json:"default_enabled"`
}

CapabilityDescriptor is the registered metadata for one capability.

The registry stores descriptors; the capabilities service combines them with availability checks and per-team toggle state to produce the final list of Capability values served to consumers (UI, pipeline factories).

Descriptors are pure metadata: no behaviour, no backend handles. Backend availability is computed by an AvailabilityResolver at request time so a running system reflects current state (e.g. an LLM provider added at runtime makes LLM-backed capabilities Available without restart).

type CapabilityPreferences

type CapabilityPreferences struct {
	TeamID    string                  `json:"team_id"`
	Toggles   map[CapabilityType]bool `json:"toggles"`
	UpdatedAt time.Time               `json:"updated_at"`
}

CapabilityPreferences is the persisted per-team toggle state. Stored as row-per-toggle in the backing store; presented to consumers as a flat map keyed by CapabilityType.

Capabilities absent from the map fall back to the descriptor's DefaultEnabled at resolution time. This keeps the storage minimal — only explicit operator overrides are persisted.

func DefaultCapabilityPreferences

func DefaultCapabilityPreferences(teamID string) *CapabilityPreferences

DefaultCapabilityPreferences returns an empty preferences struct for a team — no toggles set, every capability inherits its descriptor default.

func (*CapabilityPreferences) IsEnabled added in v0.4.0

func (p *CapabilityPreferences) IsEnabled(t CapabilityType, fallbackDefault bool) bool

IsEnabled returns the operator's preference for the given capability, defaulting to fallbackDefault when no explicit preference is set. Pass the descriptor's DefaultEnabled here so unset toggles inherit the capability's intended default rather than always returning false.

type CapabilityRegistry added in v0.4.0

type CapabilityRegistry interface {
	// Register adds a descriptor to the registry. Returns an error when a
	// descriptor with the same Type is already registered — duplicate
	// registration is a programmer error and must surface, not silently
	// overwrite.
	Register(d CapabilityDescriptor) error

	// All returns every registered descriptor. The returned slice is a
	// copy; mutation does not affect the registry.
	All() []CapabilityDescriptor

	// Get returns the descriptor for the given type, or false if no
	// descriptor with that type is registered.
	Get(t CapabilityType) (CapabilityDescriptor, bool)
}

CapabilityRegistry is a runtime registry of capability descriptors. The capabilities service iterates the registry to know which capabilities exist; consumers (built-in or add-on) register their descriptors at startup before the service handles requests.

Registration order is not significant — descriptors expose their own dependency graph via DependsOn / Grants and the resolver walks it generically. Adding a new capability requires only registering a descriptor; no service-side code changes.

Implementations must be safe for concurrent reads. Registration is expected to happen during startup (single-goroutine) and is not performance-critical.

func NewCapabilityRegistry added in v0.4.0

func NewCapabilityRegistry() CapabilityRegistry

NewCapabilityRegistry creates an empty in-memory registry.

type CapabilityType

type CapabilityType string

CapabilityType is the open-string identifier for a capability. Built-in capabilities ship with constants below; add-ons (or Core consumers wiring their own pipeline stages) MAY register additional types via the CapabilityRegistry without modifying this file.

const (
	// Indexing capabilities (user controls).
	CapabilityTextIndexing      CapabilityType = "text_indexing"      // Enables BM25 search
	CapabilityEmbeddingIndexing CapabilityType = "embedding_indexing" // Enables vector search

	// Search capabilities (derived from indexing, toggleable).
	CapabilityBM25Search   CapabilityType = "bm25_search"   // Keyword search
	CapabilityVectorSearch CapabilityType = "vector_search" // Semantic search

	// LLM-powered search enhancements (require LLM provider).
	CapabilityQueryExpansion CapabilityType = "query_expansion" // Expands queries with related terms
	CapabilityQueryRewriting CapabilityType = "query_rewriting" // Reformulates queries for better matching
	CapabilitySummarization  CapabilityType = "summarization"   // Generates result snippets
)

type Change

type Change struct {
	Type        ChangeType                                `json:"type"`
	Document    *Document                                 `json:"document,omitempty"`   // For added/modified
	Content     string                                    `json:"content,omitempty"`    // Eager content; ignored when LoadContent is set
	LoadContent func(ctx context.Context) (string, error) `json:"-"`                    // Optional. When set, invoked once during processing to fetch content lazily.
	DeletedID   string                                    `json:"deleted_id,omitempty"` // For deleted
	ExternalID  string                                    `json:"external_id"`          // ID from source system
}

Change represents a document change from a connector.

Content resolution: when LoadContent is non-nil, the orchestrator invokes it lazily during processing and uses the returned bytes as the document content; otherwise the eager Content field is used. Connectors with non-trivial body fetches SHOULD populate LoadContent rather than downloading inline during FetchChanges, so listing returns quickly and downloads parallelise with the per-document worker pool.

LoadContent is intentionally not serialised — the closure captures connector-scoped state (HTTP client, auth) that is not portable across process boundaries.

type ChangePasswordRequest

type ChangePasswordRequest struct {
	CurrentPassword string `json:"current_password"`
	NewPassword     string `json:"new_password"`
}

ChangePasswordRequest represents a password change by authenticated user

type ChangeType

type ChangeType string

ChangeType indicates what happened to a document

const (
	ChangeTypeAdded    ChangeType = "added"
	ChangeTypeModified ChangeType = "modified"
	ChangeTypeDeleted  ChangeType = "deleted"
)

type ClientRegistrationRequest added in v0.2.1

type ClientRegistrationRequest struct {
	Name                    string           `json:"client_name"`
	RedirectURIs            []string         `json:"redirect_uris"`
	GrantTypes              []OAuthGrantType `json:"grant_types,omitempty"`
	ResponseTypes           []string         `json:"response_types,omitempty"`
	Scopes                  ScopeList        `json:"scope,omitempty"` // Space-delimited string or array
	ApplicationType         string           `json:"application_type,omitempty"`
	TokenEndpointAuthMethod string           `json:"token_endpoint_auth_method,omitempty"`
}

ClientRegistrationRequest represents a dynamic client registration request (RFC 7591)

type ClientRegistrationResponse added in v0.2.1

type ClientRegistrationResponse struct {
	ClientID                string           `json:"client_id"`
	ClientSecret            string           `json:"client_secret,omitempty"` // Only returned once for confidential clients
	Name                    string           `json:"client_name"`
	RedirectURIs            []string         `json:"redirect_uris"`
	GrantTypes              []OAuthGrantType `json:"grant_types"`
	ResponseTypes           []string         `json:"response_types"`
	Scopes                  ScopeList        `json:"scope"`
	ApplicationType         string           `json:"application_type"`
	TokenEndpointAuthMethod string           `json:"token_endpoint_auth_method"`
	ClientIDIssuedAt        int64            `json:"client_id_issued_at"` // Unix timestamp
}

ClientRegistrationResponse represents the response from dynamic client registration

type CompletionRequest added in v0.2.2

type CompletionRequest struct {
	// SystemPrompt sets the system-level instructions for the LLM
	SystemPrompt string `json:"system_prompt"`

	// UserPrompt is the user's input or query to the LLM
	UserPrompt string `json:"user_prompt"`

	// MaxTokens limits the length of the generated response
	// If 0, the LLM provider's default is used
	MaxTokens int `json:"max_tokens,omitempty"`

	// Temperature controls randomness in the response (0.0 to 2.0)
	// Lower values make output more focused and deterministic
	// Higher values make output more creative and random
	Temperature float64 `json:"temperature,omitempty"`

	// ResponseSchema is an optional JSON Schema that constrains the LLM output
	// to a specific structure. Used for structured data extraction.
	// The actual schema format depends on the LLM provider.
	ResponseSchema any `json:"response_schema,omitempty"`
}

CompletionRequest represents a request to an LLM for text completion. This is a pure domain type with no external dependencies.

func NewCompletionRequest added in v0.2.2

func NewCompletionRequest(systemPrompt, userPrompt string) CompletionRequest

NewCompletionRequest creates a simple completion request with just prompts. Use the struct directly for more control over optional parameters.

func (CompletionRequest) Validate added in v0.2.2

func (r CompletionRequest) Validate() error

Validate checks if the completion request is valid.

func (CompletionRequest) WithMaxTokens added in v0.2.2

func (r CompletionRequest) WithMaxTokens(maxTokens int) CompletionRequest

WithMaxTokens returns a new request with MaxTokens set.

func (CompletionRequest) WithResponseSchema added in v0.2.2

func (r CompletionRequest) WithResponseSchema(schema any) CompletionRequest

WithResponseSchema returns a new request with a JSON Schema for structured output.

func (CompletionRequest) WithTemperature added in v0.2.2

func (r CompletionRequest) WithTemperature(temperature float64) CompletionRequest

WithTemperature returns a new request with Temperature set.

type CompletionResponse added in v0.2.2

type CompletionResponse struct {
	// Content is the generated text from the LLM
	Content string `json:"content"`

	// Usage tracks token consumption for this request
	Usage TokenUsage `json:"usage"`
}

CompletionResponse represents the LLM's response to a completion request.

func (CompletionResponse) HasUsage added in v0.2.2

func (r CompletionResponse) HasUsage() bool

HasUsage returns true if token usage information is available.

func (CompletionResponse) IsEmpty added in v0.2.2

func (r CompletionResponse) IsEmpty() bool

IsEmpty returns true if the response has no content.

type CompositeAvailabilityResolver added in v0.4.0

type CompositeAvailabilityResolver struct {
	// contains filtered or unexported fields
}

CompositeAvailabilityResolver chains multiple resolvers; the first one to return a definitive answer for a known capability wins. This lets Core ship a base resolver and add-ons compose their own rules without modifying Core's resolver.

"Definitive answer" semantics: each resolver in the chain reports both a known/unknown signal and an availability bit. Use the AnswerableResolver wrapper or implement IsKnown directly when you need chain-aware resolution.

For the common case of "either resolver says yes → yes", use resolverAny instead.

func NewCompositeAvailabilityResolver added in v0.4.0

func NewCompositeAvailabilityResolver(resolvers ...AvailabilityResolver) *CompositeAvailabilityResolver

NewCompositeAvailabilityResolver builds a chain. The first resolver that returns true for a given type wins; if none return true the answer is false.

This means the chain is "OR" semantics: the capability is available if ANY resolver says it is. Suitable for the common pattern of a base resolver + add-on resolvers each contributing knowledge of their own capability set.

func (*CompositeAvailabilityResolver) IsAvailable added in v0.4.0

IsAvailable implements AvailabilityResolver.

type Connection

type Connection struct {
	ID           string       `json:"id"`
	Name         string       `json:"name"`
	Platform     PlatformType `json:"platform"`      // NEW: authentication boundary
	ProviderType ProviderType `json:"provider_type"` // kept for backward compat
	AuthMethod   AuthMethod   `json:"auth_method"`

	// Secrets contains decrypted secret values (never persisted as-is)
	// This is populated when fetching from store, nil when listing
	Secrets *ConnectionSecrets `json:"-"`

	// OAuth metadata (non-secret, safe to expose)
	OAuthTokenType string     `json:"oauth_token_type,omitempty"`
	OAuthExpiry    *time.Time `json:"oauth_expiry,omitempty"`
	OAuthScopes    []string   `json:"oauth_scopes,omitempty"`

	// AccountID is the provider account identifier (email, username)
	// Used for display and uniqueness constraint
	AccountID string `json:"account_id,omitempty"`

	// TenantID identifies the organization-level identity scope this connection
	// is valid for (e.g. a directory tenant, customer ID, or org name depending
	// on the provider). Only set for AuthMethodAppOnly connections; empty for
	// delegated connections. The format is provider-defined.
	TenantID string `json:"tenant_id,omitempty"`

	// AppCredentialsRef is a reference into the secret store pointing to the
	// long-lived application credential (client secret or certificate) used for
	// AuthMethodAppOnly flows. It is an opaque identifier — the credential
	// itself is never stored on the Connection record.
	AppCredentialsRef string `json:"app_credentials_ref,omitempty"`

	CreatedAt  time.Time  `json:"created_at"`
	UpdatedAt  time.Time  `json:"updated_at"`
	LastUsedAt *time.Time `json:"last_used_at,omitempty"`
}

Connection represents a connector connection with stored credentials. A connection is the authenticated link to a provider (GitHub, Google, etc.) Sources reference connections to get their authentication context.

func (*Connection) GetAccessToken

func (c *Connection) GetAccessToken() string

GetAccessToken returns the access token if available. For OAuth2: returns the access token. For API Key/PAT: returns the API key. For AppOnly: returns the in-memory cached access token if set, otherwise empty string. The actual token fetch is performed by the TokenProvider; this method is a convenience accessor for an already-cached token.

func (*Connection) HasScope added in v0.2.1

func (c *Connection) HasScope(scope string) bool

HasScope checks if the connection has a specific OAuth scope

func (*Connection) HasSecrets

func (c *Connection) HasSecrets() bool

HasSecrets returns true if the connection has secrets loaded.

func (*Connection) IsExpired

func (c *Connection) IsExpired() bool

IsExpired returns true if OAuth tokens have expired.

func (*Connection) MissingScopes added in v0.2.1

func (c *Connection) MissingScopes(required []string) []string

MissingScopes returns scopes from required that are not in OAuthScopes

func (*Connection) NeedsRefresh

func (c *Connection) NeedsRefresh() bool

NeedsRefresh returns true if OAuth tokens should be refreshed. Returns true if within 5 minutes of expiry.

func (*Connection) ToSummary

func (c *Connection) ToSummary() *ConnectionSummary

ToSummary converts Connection to ConnectionSummary.

type ConnectionSecrets

type ConnectionSecrets struct {
	// OAuth2 tokens
	AccessToken  string `json:"access_token,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`

	// API Key (for api_key auth method)
	APIKey string `json:"api_key,omitempty"`

	// Service Account JSON (for service_account auth method, e.g., Google)
	ServiceAccountJSON string `json:"service_account_json,omitempty"`
}

ConnectionSecrets contains decrypted secret values. These are encrypted before storage and decrypted on retrieval.

type ConnectionSummary

type ConnectionSummary struct {
	ID                string       `json:"id"`
	Name              string       `json:"name"`
	Platform          PlatformType `json:"platform"` // NEW
	ProviderType      ProviderType `json:"provider_type"`
	AuthMethod        AuthMethod   `json:"auth_method"`
	AccountID         string       `json:"account_id,omitempty"`
	OAuthExpiry       *time.Time   `json:"oauth_expiry,omitempty"`
	TenantID          string       `json:"tenant_id,omitempty"`
	AppCredentialsRef string       `json:"app_credentials_ref,omitempty"`
	CreatedAt         time.Time    `json:"created_at"`
	LastUsedAt        *time.Time   `json:"last_used_at,omitempty"`
}

ConnectionSummary is a safe view without secrets for listing.

type Container

type Container struct {
	ID       string         `json:"id"`
	Name     string         `json:"name"`
	Type     string         `json:"type,omitempty"`
	Metadata map[string]any `json:"metadata,omitempty"`
}

Container represents a selectable unit within a connection. Examples: a repository, a Slack channel, a Drive folder, a Notion database.

Metadata mirrors driven.Container.Metadata's map[string]any shape — values arrive from connector listings (booleans, ints, strings) and the UI re-sends them on /sources POST. A typed map[string]string here would reject any container whose lister reports non-string metadata (e.g. GitHub's `private`/`fork` booleans, OneDrive's int `child_count`).

type CredentialSummary

type CredentialSummary struct {
	ID           string       `json:"id"`
	ProviderType ProviderType `json:"provider_type"`
	AuthMethod   AuthMethod   `json:"auth_method"`
	Name         string       `json:"name"`
	HasToken     bool         `json:"has_token"`
	TokenExpiry  *time.Time   `json:"token_expiry,omitempty"`
	CreatedAt    time.Time    `json:"created_at"`
}

CredentialSummary provides a safe view without sensitive data

type Credentials

type Credentials struct {
	ID           string       `json:"id"`
	ProviderType ProviderType `json:"provider_type"`
	AuthMethod   AuthMethod   `json:"auth_method"`
	Name         string       `json:"name"` // User-friendly name

	// OAuth2 fields
	AccessToken  string     `json:"-"` // Never serialize
	RefreshToken string     `json:"-"` // Never serialize
	TokenExpiry  *time.Time `json:"token_expiry,omitempty"`
	Scopes       []string   `json:"scopes,omitempty"`

	// API Key / PAT
	APIKey string `json:"-"` // Never serialize

	// Basic Auth
	Username string `json:"-"` // Never serialize
	Password string `json:"-"` // Never serialize

	// Service Account (e.g., Google)
	ServiceAccountJSON string `json:"-"` // Never serialize

	// Metadata
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	CreatedBy string    `json:"created_by"` // User ID
}

Credentials stores authentication credentials for a source connector

func (*Credentials) IsExpired

func (c *Credentials) IsExpired() bool

IsExpired checks if OAuth tokens have expired

func (*Credentials) NeedsRefresh

func (c *Credentials) NeedsRefresh() bool

NeedsRefresh checks if tokens should be refreshed (within 5 min of expiry)

func (*Credentials) ToSummary

func (c *Credentials) ToSummary() *CredentialSummary

ToSummary converts Credentials to CredentialSummary

type Document

type Document struct {
	ID         string            `json:"id"`
	SourceID   string            `json:"source_id"`
	ExternalID string            `json:"external_id"` // ID from the source system
	Path       string            `json:"path"`        // Path or URL in source
	Title      string            `json:"title"`
	MimeType   string            `json:"mime_type"`
	Metadata   map[string]string `json:"metadata"`
	CreatedAt  time.Time         `json:"created_at"`
	UpdatedAt  time.Time         `json:"updated_at"`
	IndexedAt  time.Time         `json:"indexed_at"`
}

Document represents an indexed document from a source

type DocumentContent

type DocumentContent struct {
	DocumentID string            `json:"document_id"`
	SourceID   string            `json:"source_id"`
	Title      string            `json:"title"`
	Body       string            `json:"body"`
	Path       string            `json:"path"`
	MimeType   string            `json:"mime_type"`
	Metadata   map[string]string `json:"metadata"`
}

DocumentContent holds the full content of a document for indexing.

type DocumentIDFilter added in v0.3.0

type DocumentIDFilter struct {
	Apply bool     `json:"apply"`
	IDs   []string `json:"ids"`
}

DocumentIDFilter expresses an authoritative document-id filter with three-case semantics designed to close a fail-open hole in the permission-aware search path.

A nil *DocumentIDFilter means "no filter" (the field is unset).

A non-nil *DocumentIDFilter has two further cases controlled by Apply:

  • Apply == false: no filter is applied. (Equivalent to a nil pointer; normalise callers should prefer nil to express this.)
  • Apply == true && len(IDs) == 0: authoritative deny-all. Downstream MUST return zero results; adapters MUST NOT silently treat this as "no filter".
  • Apply == true && len(IDs) > 0: authoritative allow-list. Downstream MUST restrict results to exactly these IDs.

This type exists specifically because bare []string conflates "no filter" and "deny all" — a provider that returns an empty slice for a user with access to zero documents would otherwise be silently granted access to every indexed document.

func AllowDocumentIDs added in v0.3.0

func AllowDocumentIDs(ids []string) *DocumentIDFilter

AllowDocumentIDs returns a filter restricting results to the given IDs. If ids is empty, this is equivalent to DenyAllDocumentIDFilter.

func DenyAllDocumentIDFilter added in v0.3.0

func DenyAllDocumentIDFilter() *DocumentIDFilter

DenyAllDocumentIDFilter returns a filter that matches zero documents.

func (*DocumentIDFilter) IsAllowList added in v0.3.0

func (f *DocumentIDFilter) IsAllowList() bool

IsAllowList reports whether the filter is an active allow-list with at least one ID. A nil receiver or Apply==false returns false.

func (*DocumentIDFilter) IsDenyAll added in v0.3.0

func (f *DocumentIDFilter) IsDenyAll() bool

IsDenyAll reports whether the filter, if applied, denies all documents. A nil receiver or Apply==false returns false (no filter means "no denial").

type EmbeddingConfig

type EmbeddingConfig struct {
	Provider   AIProvider `json:"provider"`
	Model      string     `json:"model"`
	Dimensions int        `json:"dimensions"`
	BatchSize  int        `json:"batch_size"`
}

EmbeddingConfig holds embedding model configuration

func DefaultEmbeddingConfig

func DefaultEmbeddingConfig() *EmbeddingConfig

DefaultEmbeddingConfig returns default embedding configuration

type EmbeddingSettings

type EmbeddingSettings struct {
	Provider AIProvider `json:"provider"`
	Model    string     `json:"model"`
}

EmbeddingSettings configures the embedding service API keys and base URLs come from environment variables, not stored here

func (*EmbeddingSettings) IsConfigured

func (e *EmbeddingSettings) IsConfigured() bool

IsConfigured returns true if embedding settings are properly configured Note: API key availability is checked at the infrastructure layer (config package)

type Filters

type Filters struct {
	MimeTypes  []string   `json:"mime_types,omitempty"`
	DateAfter  *time.Time `json:"date_after,omitempty"`
	DateBefore *time.Time `json:"date_before,omitempty"`
}

Filters provides additional search filters

type JobDetail

type JobDetail struct {
	// Task is the main task information
	Task *Task `json:"task"`

	// SourceName is the name of the source (if applicable)
	SourceName string `json:"source_name,omitempty"`

	// ExecutionLogs contains log entries for this job (future enhancement)
	ExecutionLogs []string `json:"execution_logs,omitempty"`

	// RetryHistory shows previous retry attempts
	RetryHistory []*RetryAttempt `json:"retry_history,omitempty"`
}

JobDetail represents detailed information about a specific job This is a value object that combines task information with execution context

func NewJobDetail

func NewJobDetail(task *Task) *JobDetail

NewJobDetail creates a new job detail value object

func (*JobDetail) WithExecutionLogs

func (jd *JobDetail) WithExecutionLogs(logs []string) *JobDetail

WithExecutionLogs adds execution logs to the job detail

func (*JobDetail) WithRetryHistory

func (jd *JobDetail) WithRetryHistory(history []*RetryAttempt) *JobDetail

WithRetryHistory adds retry history to the job detail

func (*JobDetail) WithSourceName

func (jd *JobDetail) WithSourceName(sourceName string) *JobDetail

WithSourceName adds the source name to the job detail

type JobHistory

type JobHistory struct {
	// Jobs is the list of historical job executions
	Jobs []*Task `json:"jobs"`

	// TotalCount is the total number of jobs matching the query
	TotalCount int64 `json:"total_count"`

	// HasMore indicates if there are more jobs beyond the current page
	HasMore bool `json:"has_more"`
}

JobHistory represents a summarized view of job execution history This is a value object used for the job history endpoint

func NewJobHistory

func NewJobHistory(jobs []*Task, totalCount int64, limit int) *JobHistory

NewJobHistory creates a new job history value object

type JobStats

type JobStats struct {
	// Total jobs across all statuses
	TotalJobs int64 `json:"total_jobs"`

	// PendingJobs is the number of jobs waiting to be processed
	PendingJobs int64 `json:"pending_jobs"`

	// ProcessingJobs is the number of jobs currently being processed
	ProcessingJobs int64 `json:"processing_jobs"`

	// CompletedJobs is the number of successfully completed jobs
	CompletedJobs int64 `json:"completed_jobs"`

	// FailedJobs is the number of failed jobs
	FailedJobs int64 `json:"failed_jobs"`

	// SuccessRate is the percentage of successful jobs (0-100)
	SuccessRate float64 `json:"success_rate"`

	// AverageDuration is the average job duration in milliseconds
	AverageDuration float64 `json:"average_duration_ms"`

	// TotalRetries is the total number of retry attempts
	TotalRetries int64 `json:"total_retries"`

	// JobsByType shows the breakdown by task type
	JobsByType map[TaskType]int64 `json:"jobs_by_type"`

	// Period is the time range for these statistics
	Period AnalyticsPeriod `json:"period"`
}

JobStats represents aggregated job statistics This is a value object (no identity) representing computed statistics

func NewJobStats

func NewJobStats(period AnalyticsPeriod) *JobStats

NewJobStats creates a new job statistics value object

func (*JobStats) CalculateSuccessRate

func (js *JobStats) CalculateSuccessRate()

CalculateSuccessRate computes the success rate percentage

type LLMSettings

type LLMSettings struct {
	Provider AIProvider `json:"provider"`
	Model    string     `json:"model"`
}

LLMSettings configures the LLM service API keys and base URLs come from environment variables, not stored here

func (*LLMSettings) IsConfigured

func (l *LLMSettings) IsConfigured() bool

IsConfigured returns true if LLM settings are properly configured Note: API key availability is checked at the infrastructure layer (config package)

type LoginRequest

type LoginRequest struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

LoginRequest represents a login attempt

type LoginResponse

type LoginResponse struct {
	Token        string       `json:"token"`
	RefreshToken string       `json:"refresh_token,omitempty"`
	ExpiresAt    time.Time    `json:"expires_at"`
	User         *UserSummary `json:"user"`
}

LoginResponse is returned after successful authentication

type OAuthAccessToken added in v0.2.1

type OAuthAccessToken struct {
	ID        string    `json:"id"` // jti claim
	ClientID  string    `json:"client_id"`
	UserID    string    `json:"user_id"`
	Scopes    []string  `json:"scopes"`
	Audience  string    `json:"audience"`   // Resource indicator (MCP server URL)
	ExpiresAt time.Time `json:"expires_at"` // Short-lived (15 min default)
	CreatedAt time.Time `json:"created_at"`
	Revoked   bool      `json:"revoked"`
}

OAuthAccessToken represents an OAuth 2.0 access token

func (*OAuthAccessToken) IsExpired added in v0.2.1

func (t *OAuthAccessToken) IsExpired() bool

IsExpired checks if the access token has expired

func (*OAuthAccessToken) IsValid added in v0.2.1

func (t *OAuthAccessToken) IsValid() bool

IsValid checks if the access token is valid (not expired and not revoked)

type OAuthClient added in v0.2.1

type OAuthClient struct {
	ID                      string           `json:"id"`                         // client_id (UUID)
	SecretHash              string           `json:"-"`                          // bcrypt hash of client_secret (empty for public clients)
	Name                    string           `json:"name"`                       // Human-readable name
	RedirectURIs            []string         `json:"redirect_uris"`              // Registered redirect URIs
	GrantTypes              []OAuthGrantType `json:"grant_types"`                // Allowed grant types
	ResponseTypes           []string         `json:"response_types"`             // Allowed response types (e.g., "code")
	Scopes                  []string         `json:"scopes"`                     // Allowed scopes
	ApplicationType         string           `json:"application_type"`           // "native" or "web"
	TokenEndpointAuthMethod string           `json:"token_endpoint_auth_method"` // "client_secret_post" or "none" (public)
	Active                  bool             `json:"active"`                     // Whether the client is active
	CreatedAt               time.Time        `json:"created_at"`
	UpdatedAt               time.Time        `json:"updated_at"`
}

OAuthClient represents a registered third-party OAuth 2.0 client application

func (*OAuthClient) HasGrantType added in v0.2.1

func (c *OAuthClient) HasGrantType(gt OAuthGrantType) bool

HasGrantType checks if the client is allowed to use the given grant type

func (*OAuthClient) HasRedirectURI added in v0.2.1

func (c *OAuthClient) HasRedirectURI(uri string) bool

HasRedirectURI validates if the given URI is in the client's registered redirect URIs

func (*OAuthClient) HasScope added in v0.2.1

func (c *OAuthClient) HasScope(scope string) bool

HasScope checks if the client is allowed to request the given scope

func (*OAuthClient) IsPublic added in v0.2.1

func (c *OAuthClient) IsPublic() bool

IsPublic returns true if the client is a public client (no client secret required)

func (*OAuthClient) ValidateScopes added in v0.2.1

func (c *OAuthClient) ValidateScopes(requested []string) []string

ValidateScopes returns a list of invalid scopes from the requested list

type OAuthGrantType added in v0.2.1

type OAuthGrantType string

OAuthGrantType represents the OAuth 2.0 grant type

const (
	GrantTypeAuthorizationCode OAuthGrantType = "authorization_code"
	GrantTypeRefreshToken      OAuthGrantType = "refresh_token"
)

type OAuthRefreshToken added in v0.2.1

type OAuthRefreshToken struct {
	ID            string    `json:"id"`
	AccessTokenID string    `json:"access_token_id"` // Associated access token
	ClientID      string    `json:"client_id"`
	UserID        string    `json:"user_id"`
	Scopes        []string  `json:"scopes"`
	Audience      string    `json:"audience"`
	ExpiresAt     time.Time `json:"expires_at"` // 30 days
	CreatedAt     time.Time `json:"created_at"`
	Revoked       bool      `json:"revoked"`
	RotatedTo     string    `json:"rotated_to"` // New token ID after rotation (empty if current)
}

OAuthRefreshToken represents an OAuth 2.0 refresh token with rotation support

func (*OAuthRefreshToken) IsExpired added in v0.2.1

func (t *OAuthRefreshToken) IsExpired() bool

IsExpired checks if the refresh token has expired

func (*OAuthRefreshToken) IsRotated added in v0.2.1

func (t *OAuthRefreshToken) IsRotated() bool

IsRotated checks if the refresh token has been rotated to a new token

func (*OAuthRefreshToken) IsValid added in v0.2.1

func (t *OAuthRefreshToken) IsValid() bool

IsValid checks if the refresh token is valid (not expired, not revoked, and not rotated)

type PasswordResetConfirm

type PasswordResetConfirm struct {
	Token       string `json:"token"`
	NewPassword string `json:"new_password"`
}

PasswordResetConfirm represents a password reset confirmation

type PasswordResetRequest

type PasswordResetRequest struct {
	Email string `json:"email"`
}

PasswordResetRequest represents a password reset request

type PipelinePhase

type PipelinePhase string

PipelinePhase identifies which phase of the pipeline a capability belongs to. The registry uses this to group capabilities for the UI; consumers that gate on phase (e.g. a stage factory) may also use it.

const (
	PipelinePhaseIndexing PipelinePhase = "indexing"
	PipelinePhaseSearch   PipelinePhase = "search"
)

type PlatformType added in v0.2.1

type PlatformType string

PlatformType identifies an authentication boundary (OAuth client) Multiple services can share a platform (e.g., google_drive and google_docs both use google)

const (
	// Only implemented platforms (1:1 with providers for now)
	PlatformGitHub    PlatformType = "github"
	PlatformLocalFS   PlatformType = "localfs"
	PlatformNotion    PlatformType = "notion"
	PlatformMicrosoft PlatformType = "microsoft"
)

func PlatformFor added in v0.2.1

func PlatformFor(provider ProviderType) PlatformType

PlatformFor returns the platform that owns a given service/provider. For 1:1 connectors (all current ones), platform == provider. Multi-service platforms (Google, Microsoft, Atlassian) map multiple ProviderTypes to one PlatformType.

Custom mappings registered via RegisterPlatformMapping take precedence over the built-in switch. Unknown providers fall through to PlatformType(provider).

type ProviderConfig

type ProviderConfig struct {
	ProviderType ProviderType

	// Decrypted secrets (never persisted as-is)
	Secrets *ProviderSecrets

	// Non-secret configuration (can override defaults)
	AuthURL     string   // OAuth authorization URL
	TokenURL    string   // OAuth token URL
	Scopes      []string // OAuth scopes
	RedirectURI string   // OAuth callback URL

	// Metadata
	Enabled   bool
	CreatedAt time.Time
	UpdatedAt time.Time
}

ProviderConfig represents OAuth app configuration for a provider type. One config per provider - multiple installations can use the same config.

func (*ProviderConfig) IsConfigured

func (p *ProviderConfig) IsConfigured() bool

IsConfigured returns true if the provider has credentials set.

type ProviderConfigSummary

type ProviderConfigSummary struct {
	ProviderType ProviderType
	Enabled      bool
	HasSecrets   bool // true if secrets are configured
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

ProviderConfigSummary is a safe view without secrets (for listing).

type ProviderMetadata added in v0.3.0

type ProviderMetadata struct {
	Type        ProviderType
	DisplayName string
	Platform    PlatformType
	IconID      string
}

ProviderMetadata describes presentation details for a ProviderType. This is the single source of truth adapters consult when they need to render provider-facing UI (icons, display names) — MCP widgets, the Next.js admin UI, and any future adapter should all read from here rather than hard-coding provider strings.

IconID is a stable identifier, not a filesystem path. Each adapter maps IconID to its own asset source: the MCP adapter serves a PNG from embed.FS, the Next.js UI bundles an SVG, a hypothetical CLI renders an ASCII glyph. Keeping the domain ignorant of paths preserves the hexagonal boundary.

func AllProviderMetadata added in v0.3.0

func AllProviderMetadata() []ProviderMetadata

AllProviderMetadata returns metadata for every known provider in a stable order. Callers must not mutate the returned slice.

func MetadataFor added in v0.3.0

func MetadataFor(p ProviderType) (ProviderMetadata, bool)

MetadataFor returns metadata for a ProviderType. The second return value is false if the provider is not registered.

type ProviderSecrets

type ProviderSecrets struct {
	ClientID     string `json:"client_id,omitempty"`
	ClientSecret string `json:"client_secret,omitempty"`
	APIKey       string `json:"api_key,omitempty"` // For non-OAuth providers
}

ProviderSecrets contains decrypted secret values. Encrypted before storage using same AES-GCM as installations.

type ProviderType

type ProviderType string

ProviderType identifies a data source provider

const (
	// Only implemented connectors
	ProviderTypeGitHub   ProviderType = "github"
	ProviderTypeLocalFS  ProviderType = "localfs"
	ProviderTypeNotion   ProviderType = "notion"
	ProviderTypeOneDrive ProviderType = "onedrive"
)

func ServicesFor added in v0.2.1

func ServicesFor(platform PlatformType) []ProviderType

ServicesFor returns all services under a platform. The result combines the built-in mapping with anything registered via RegisterPlatformMapping.

type QueryFrequency

type QueryFrequency struct {
	Query string `json:"query"`
	Count int64  `json:"count"`
}

QueryFrequency represents how often a query appears

type RefreshRequest

type RefreshRequest struct {
	RefreshToken string `json:"refresh_token"`
}

RefreshRequest represents a token refresh attempt

type RetryAttempt

type RetryAttempt struct {
	// Attempt is the attempt number (1-indexed)
	Attempt int `json:"attempt"`

	// Error is the error message from this attempt
	Error string `json:"error"`

	// Timestamp is when this attempt occurred
	Timestamp time.Time `json:"timestamp"`
}

RetryAttempt represents a single retry attempt for a job

type RevokeRequest added in v0.2.1

type RevokeRequest struct {
	Token         string `json:"token"`
	TokenTypeHint string `json:"token_type_hint,omitempty"` // "access_token" or "refresh_token"
}

RevokeRequest represents a token revocation request (RFC 7009)

type Role

type Role string

Role defines user permission level

const (
	RoleAdmin  Role = "admin"  // Manage users, sources, settings
	RoleMember Role = "member" // Search, view documents
	RoleViewer Role = "viewer" // Search only (future)
)

type RuntimeConfig

type RuntimeConfig struct {

	// Static (set at startup, read-only)
	SessionBackend string // "redis" or "postgres"
	// contains filtered or unexported fields
}

RuntimeConfig tracks which services are available at runtime. This is determined at startup and can be updated dynamically for AI services. Thread-safe for concurrent access.

func NewRuntimeConfig

func NewRuntimeConfig(sessionBackend string) *RuntimeConfig

NewRuntimeConfig creates a new RuntimeConfig with initial values

func (*RuntimeConfig) CanDoHybridSearch

func (c *RuntimeConfig) CanDoHybridSearch() bool

CanDoHybridSearch returns true if hybrid search is possible

func (*RuntimeConfig) CanDoLLMAssisted

func (c *RuntimeConfig) CanDoLLMAssisted() bool

CanDoLLMAssisted returns true if LLM features are available

func (*RuntimeConfig) CanDoSemanticSearch

func (c *RuntimeConfig) CanDoSemanticSearch() bool

CanDoSemanticSearch returns true if semantic search is possible

func (*RuntimeConfig) EffectiveSearchMode

func (c *RuntimeConfig) EffectiveSearchMode() SearchMode

EffectiveSearchMode returns the best available search mode

func (*RuntimeConfig) EmbeddingAvailable

func (c *RuntimeConfig) EmbeddingAvailable() bool

EmbeddingAvailable returns whether embedding service is available

func (*RuntimeConfig) LLMAvailable

func (c *RuntimeConfig) LLMAvailable() bool

LLMAvailable returns whether LLM service is available

func (*RuntimeConfig) SetEmbeddingAvailable

func (c *RuntimeConfig) SetEmbeddingAvailable(available bool)

SetEmbeddingAvailable updates the embedding availability flag

func (*RuntimeConfig) SetLLMAvailable

func (c *RuntimeConfig) SetLLMAvailable(available bool)

SetLLMAvailable updates the LLM availability flag

type ScheduledTask

type ScheduledTask struct {
	// ID is the unique identifier for this scheduled task
	ID string `json:"id"`

	// Name is a human-readable name for the task
	Name string `json:"name"`

	// Type is the task type to create when triggered
	Type TaskType `json:"type"`

	// TeamID is the team this schedule belongs to
	TeamID string `json:"team_id"`

	// Interval is how often to run the task
	Interval time.Duration `json:"interval"`

	// Enabled indicates if the schedule is active
	Enabled bool `json:"enabled"`

	// LastRun is when the task was last triggered
	LastRun *time.Time `json:"last_run,omitempty"`

	// NextRun is when the task should next be triggered
	NextRun time.Time `json:"next_run"`

	// LastError contains the last error if the scheduled task failed
	LastError string `json:"last_error,omitempty"`
}

ScheduledTask represents a recurring task configuration

func DefaultSchedulerConfig

func DefaultSchedulerConfig(teamID string, intervalMinutes int) []*ScheduledTask

DefaultSchedulerConfig returns the default scheduled tasks with custom interval If intervalMinutes is 0, defaults to 60 minutes

func NewScheduledTask

func NewScheduledTask(id, name string, taskType TaskType, teamID string, interval time.Duration) *ScheduledTask

NewScheduledTask creates a new scheduled task

func (*ScheduledTask) IsDue

func (s *ScheduledTask) IsDue() bool

IsDue returns true if the scheduled task should be triggered

func (*ScheduledTask) UpdateNextRun

func (s *ScheduledTask) UpdateNextRun()

UpdateNextRun calculates the next run time after execution

type ScopeList added in v0.2.1

type ScopeList []string

ScopeList is a []string that can unmarshal from either a JSON string (space-delimited, per RFC 7591) or a JSON array of strings.

func (ScopeList) MarshalJSON added in v0.2.1

func (s ScopeList) MarshalJSON() ([]byte, error)

func (*ScopeList) UnmarshalJSON added in v0.2.1

func (s *ScopeList) UnmarshalJSON(data []byte) error

type SearchAnalytics

type SearchAnalytics struct {
	// TotalSearches is the total number of searches in the period
	TotalSearches int64 `json:"total_searches"`

	// UniqueUsers is the number of unique users who searched
	UniqueUsers int64 `json:"unique_users"`

	// AverageDuration is the average search duration in milliseconds
	AverageDuration float64 `json:"average_duration_ms"`

	// AverageResults is the average number of results per search
	AverageResults float64 `json:"average_results"`

	// TopQueries are the most common search queries
	TopQueries []QueryFrequency `json:"top_queries"`

	// SearchesByMode shows the breakdown by search mode
	SearchesByMode map[SearchMode]int64 `json:"searches_by_mode"`

	// Period is the time range for these analytics
	Period AnalyticsPeriod `json:"period"`
}

SearchAnalytics represents aggregated search analytics data This is a value object (no identity) representing computed statistics

type SearchMetrics

type SearchMetrics struct {
	// FastSearches is the number of searches under 100ms
	FastSearches int64 `json:"fast_searches"`

	// MediumSearches is the number of searches 100ms-500ms
	MediumSearches int64 `json:"medium_searches"`

	// SlowSearches is the number of searches over 500ms
	SlowSearches int64 `json:"slow_searches"`

	// P50Duration is the 50th percentile search duration in milliseconds
	P50Duration float64 `json:"p50_duration_ms"`

	// P95Duration is the 95th percentile search duration in milliseconds
	P95Duration float64 `json:"p95_duration_ms"`

	// P99Duration is the 99th percentile search duration in milliseconds
	P99Duration float64 `json:"p99_duration_ms"`

	// ZeroResultSearches is the number of searches that returned no results
	ZeroResultSearches int64 `json:"zero_result_searches"`

	// Period is the time range for these metrics
	Period AnalyticsPeriod `json:"period"`
}

SearchMetrics represents performance metrics for searches This is a value object for monitoring search performance

type SearchMode

type SearchMode string

SearchMode determines the search strategy

const (
	SearchModeHybrid       SearchMode = "hybrid"   // BM25 + vector (default)
	SearchModeTextOnly     SearchMode = "text"     // BM25 only
	SearchModeSemanticOnly SearchMode = "semantic" // Vector only
)

func (SearchMode) RequiresEmbedding

func (mode SearchMode) RequiresEmbedding() bool

RequiresEmbedding returns true if the given search mode requires embedding

type SearchOptions

type SearchOptions struct {
	Mode             SearchMode         `json:"mode"`
	Limit            int                `json:"limit"`
	Offset           int                `json:"offset"`
	SourceIDs        []string           `json:"source_ids,omitempty"`         // Filter by sources
	DocumentIDFilter *DocumentIDFilter  `json:"document_id_filter,omitempty"` // Three-case filter (see DocumentIDFilter godoc). Nil = no filter.
	Filters          Filters            `json:"filters,omitempty"`
	BoostTerms       map[string]float64 `json:"boost_terms,omitempty"` // term -> boost factor

	// Phrases lists user-quoted phrases extracted by the query parser. Each is
	// run as a match_phrase clause against title^3 and content alongside the
	// main query terms — without this the standard analyser drops the quotes
	// and a "merge sort" search collapses into two unrelated tokens.
	Phrases []string `json:"phrases,omitempty"`

	// PipelineID optionally selects a registered search pipeline by ID.
	// Empty string falls back to the default ("default-search"). Callers
	// can register additional pipelines via PipelineRegistry and route
	// per-request by setting this field.
	PipelineID string `json:"pipeline_id,omitempty"`

	// Caller identifies the entity making the search request. Nil means the
	// origin is unknown. Stages that gate on caller identity (e.g. sensitivity
	// masker) treat nil as non-MCP (skip execution). Populated by HTTP handlers
	// (CallerSourceDirect) and MCP handlers (CallerSourceMCP).
	Caller *Caller `json:"caller,omitempty"`
}

SearchOptions configures a search request

func DefaultSearchOptions

func DefaultSearchOptions() SearchOptions

DefaultSearchOptions returns sensible defaults

type SearchQuery

type SearchQuery struct {
	// ID is the unique identifier for this search query log entry
	ID string `json:"id"`

	// TeamID is the team that performed the search
	TeamID string `json:"team_id"`

	// UserID is the user who performed the search
	UserID string `json:"user_id"`

	// Query is the search text that was submitted
	Query string `json:"query"`

	// Mode is the search mode used (hybrid, text, semantic)
	Mode SearchMode `json:"mode"`

	// ResultCount is the number of results returned
	ResultCount int `json:"result_count"`

	// Duration is how long the search took to execute (in nanoseconds)
	Duration int64 `json:"duration"`

	// SourceIDs are the source IDs that were filtered (if any)
	SourceIDs []string `json:"source_ids,omitempty"`

	// Filters are the additional filters applied
	HasFilters bool `json:"has_filters"`

	// CreatedAt is when the search was performed
	CreatedAt time.Time `json:"created_at"`
}

SearchQuery represents a logged search query for analytics This is an entity (has identity) used to track search usage and performance

func NewSearchQuery

func NewSearchQuery(teamID, userID, query string, mode SearchMode, resultCount int, duration time.Duration) *SearchQuery

NewSearchQuery creates a new search query log entry

func (*SearchQuery) GetDuration

func (sq *SearchQuery) GetDuration() time.Duration

GetDuration returns the duration as a time.Duration

func (*SearchQuery) WithFilters

func (sq *SearchQuery) WithFilters(hasFilters bool) *SearchQuery

WithFilters marks that additional filters were applied

func (*SearchQuery) WithSourceFilters

func (sq *SearchQuery) WithSourceFilters(sourceIDs []string) *SearchQuery

WithSourceFilters sets the source IDs filter

type SearchResult

type SearchResult struct {
	Query      string              `json:"query"`
	Mode       SearchMode          `json:"mode"`
	Results    []*SearchResultItem `json:"results"`
	TotalCount int                 `json:"total_count"`
	Took       time.Duration       `json:"took" swaggertype:"integer" example:"1500000"`
}

SearchResult represents the result of a search query

type SearchResultItem

type SearchResultItem struct {
	DocumentID string    `json:"document_id"`
	SourceID   string    `json:"source_id"`
	Title      string    `json:"title"`
	Path       string    `json:"path"`
	MimeType   string    `json:"mime_type"`
	Snippet    string    `json:"snippet"`
	Score      float64   `json:"score"`
	IndexedAt  time.Time `json:"indexed_at"`

	// Metadata carries arbitrary key-value annotations produced by pipeline
	// stages and copied through from pipeline.PresentedResult.Metadata by the
	// search service.
	//
	// This mirrors the Metadata fields already present on pipeline.Candidate
	// and pipeline.PresentedResult: stages can attach derived information to a
	// result (faceting hints, ranking signals, query-expansion match info,
	// stage-specific outcome flags) and have it survive the projection from
	// the pipeline's internal types to the public domain type.
	//
	// Keys are convention-based and stage-specific; the search service does
	// not interpret them, it only forwards what the presenter exposes.
	// Driving adapters (HTTP handlers, MCP servers, downstream services) read
	// keys they care about and ignore the rest.
	//
	// Stages that do not need to attach metadata may leave this nil.
	Metadata map[string]any `json:"metadata,omitempty"`
}

SearchResultItem represents a single search result at document level.

type Session

type Session struct {
	ID           string    `json:"id"`
	UserID       string    `json:"user_id"`
	Token        string    `json:"token"`
	RefreshToken string    `json:"refresh_token,omitempty"`
	ExpiresAt    time.Time `json:"expires_at"`
	CreatedAt    time.Time `json:"created_at"`
	UserAgent    string    `json:"user_agent,omitempty"`
	IPAddress    string    `json:"ip_address,omitempty"`
}

Session represents an authenticated user session

func (*Session) IsExpired

func (s *Session) IsExpired() bool

IsExpired checks if the session has expired

type Settings

type Settings struct {
	TeamID string `json:"team_id"`

	// Search Defaults
	DefaultSearchMode SearchMode `json:"default_search_mode"`
	ResultsPerPage    int        `json:"results_per_page"`
	MaxResultsPerPage int        `json:"max_results_per_page"`

	// Sync Configuration
	SyncIntervalMinutes int                    `json:"sync_interval_minutes"`
	SyncEnabled         bool                   `json:"sync_enabled"`
	SyncExclusions      *SyncExclusionSettings `json:"sync_exclusions,omitempty"`

	// Metadata
	UpdatedAt time.Time `json:"updated_at"`
	UpdatedBy string    `json:"updated_by"` // User ID
}

Settings holds team-wide configuration Note: AI configuration (provider, model, API keys) is managed via AISettings and the /settings/ai endpoint, not here. Note: Semantic/vector search is now controlled via CapabilityPreferences.

func DefaultSettings

func DefaultSettings(teamID string) *Settings

DefaultSettings returns sensible defaults for a new team

type Source

type Source struct {
	ID           string       `json:"id"`
	Name         string       `json:"name"`
	ProviderType ProviderType `json:"provider_type"`
	Config       SourceConfig `json:"config"`
	Enabled      bool         `json:"enabled"`
	CreatedAt    time.Time    `json:"created_at"`
	UpdatedAt    time.Time    `json:"updated_at"`
	CreatedBy    string       `json:"created_by"` // User ID of creator

	// ConnectionID references the connector connection for authentication
	ConnectionID string `json:"connection_id,omitempty"`

	// Containers lists the containers to index (typed)
	// Empty means index all accessible containers
	// Examples: repos for GitHub, channels for Slack, folders for Drive
	Containers []Container `json:"containers,omitempty"`
}

Source represents a data source to index

type SourceConfig

type SourceConfig struct {
	// Common fields
	CredentialID string `json:"credential_id,omitempty"`

	// GitHub/GitLab
	Owner      string   `json:"owner,omitempty"`
	Repository string   `json:"repository,omitempty"`
	Branch     string   `json:"branch,omitempty"`
	Paths      []string `json:"paths,omitempty"`

	// Slack
	Channels []string `json:"channels,omitempty"`

	// Notion
	DatabaseIDs []string `json:"database_ids,omitempty"`
	PageIDs     []string `json:"page_ids,omitempty"`

	// Confluence
	SpaceKeys []string `json:"space_keys,omitempty"`

	// Google Drive
	FolderIDs []string `json:"folder_ids,omitempty"`

	// Jira
	ProjectKeys []string `json:"project_keys,omitempty"`
	JQL         string   `json:"jql,omitempty"`

	// Generic
	BaseURL string            `json:"base_url,omitempty"`
	Extra   map[string]string `json:"extra,omitempty"`
}

SourceConfig holds provider-specific configuration

type SourceSummary

type SourceSummary struct {
	Source        *Source    `json:"source"`
	DocumentCount int        `json:"document_count"`
	LastSyncAt    *time.Time `json:"last_sync_at,omitempty"`
	SyncStatus    string     `json:"sync_status"`
}

SourceSummary provides a summary of a source's state

type SyncEvent added in v0.3.0

type SyncEvent struct {
	// ID is the unique identifier for this sync event log entry
	ID string `json:"id"`

	// TeamID is the team that owns the source being synced
	TeamID string `json:"team_id"`

	// SourceID is the source that was synced
	SourceID string `json:"source_id"`

	// SourceName is the display name of the source at sync time
	SourceName string `json:"source_name"`

	// ProviderType identifies the connector type (github, notion, etc.)
	ProviderType ProviderType `json:"provider_type"`

	// Status indicates if the sync completed successfully or failed
	Status SyncStatus `json:"status"`

	// DocumentsAdded is the number of new documents discovered
	DocumentsAdded int `json:"documents_added"`

	// DocumentsUpdated is the number of existing documents modified
	DocumentsUpdated int `json:"documents_updated"`

	// DocumentsDeleted is the number of documents removed
	DocumentsDeleted int `json:"documents_deleted"`

	// ChunksIndexed is the total number of chunks created/updated in the vector store
	ChunksIndexed int `json:"chunks_indexed"`

	// ErrorCount is the number of errors encountered during sync
	ErrorCount int `json:"error_count"`

	// ErrorMessage contains the primary error message if Status is failed
	ErrorMessage string `json:"error_message,omitempty"`

	// DurationSeconds is how long the sync operation took to execute
	DurationSeconds float64 `json:"duration_seconds"`

	// CreatedAt is when the sync event was logged
	CreatedAt time.Time `json:"created_at"`
}

SyncEvent represents a logged sync operation for audit and analytics This is an entity (has identity) used to track sync history and performance

func NewSyncEvent added in v0.3.0

func NewSyncEvent(
	teamID string,
	sourceID string,
	sourceName string,
	providerType ProviderType,
	status SyncStatus,
	stats SyncStats,
	durationSeconds float64,
) *SyncEvent

NewSyncEvent creates a new sync event log entry for a completed sync

func (*SyncEvent) GetDuration added in v0.3.0

func (se *SyncEvent) GetDuration() time.Duration

GetDuration returns the duration as a time.Duration

func (*SyncEvent) IsFailed added in v0.3.0

func (se *SyncEvent) IsFailed() bool

IsFailed returns true if the sync failed

func (*SyncEvent) IsSuccessful added in v0.3.0

func (se *SyncEvent) IsSuccessful() bool

IsSuccessful returns true if the sync completed without failure

func (*SyncEvent) TotalDocuments added in v0.3.0

func (se *SyncEvent) TotalDocuments() int

TotalDocuments returns the total number of documents affected by the sync

func (*SyncEvent) WithError added in v0.3.0

func (se *SyncEvent) WithError(errMsg string) *SyncEvent

WithError sets the error message for a failed sync event

type SyncExclusionSettings

type SyncExclusionSettings struct {
	// EnabledPatterns are the default patterns that are currently enabled
	EnabledPatterns []string `json:"enabled_patterns"`

	// DisabledPatterns are the default patterns that have been toggled off
	DisabledPatterns []string `json:"disabled_patterns"`

	// CustomPatterns are user-defined exclusion patterns
	CustomPatterns []string `json:"custom_patterns"`

	// MimeExclusions are MIME type patterns to exclude (e.g., "image/*", "font/*")
	MimeExclusions []string `json:"mime_exclusions"`
}

SyncExclusionSettings holds sync exclusion patterns configuration

func DefaultSyncExclusions

func DefaultSyncExclusions() *SyncExclusionSettings

DefaultSyncExclusions returns common file/folder patterns to exclude from sync

func (*SyncExclusionSettings) GetActiveMimeExclusions added in v0.2.1

func (s *SyncExclusionSettings) GetActiveMimeExclusions() []string

GetActiveMimeExclusions returns all active MIME type exclusion patterns

func (*SyncExclusionSettings) GetActivePatterns

func (s *SyncExclusionSettings) GetActivePatterns() []string

GetActivePatterns returns all active exclusion patterns (enabled defaults + custom)

func (*SyncExclusionSettings) HasMimeExclusions added in v0.2.1

func (s *SyncExclusionSettings) HasMimeExclusions() bool

HasMimeExclusions returns true if there are any MIME exclusion patterns

func (*SyncExclusionSettings) HasPatterns

func (s *SyncExclusionSettings) HasPatterns() bool

HasPatterns returns true if there are any active exclusion patterns

type SyncFailedDoc added in v0.4.0

type SyncFailedDoc struct {
	// SourceID is the parent source — the (source_id, external_id) pair
	// is the primary key.
	SourceID string `json:"source_id"`

	// ExternalID is the connector's identifier for the doc that failed.
	// Not the Sercha-internal document_id: failures often happen before
	// any internal row gets created.
	ExternalID string `json:"external_id"`

	// AttemptCount is the number of times we've tried this doc since the
	// row was created. Drives backoff and the terminal cutoff.
	AttemptCount int `json:"attempt_count"`

	// LastError is the error message from the most recent attempt,
	// truncated by the store layer for storage hygiene.
	LastError string `json:"last_error"`

	// LastAttemptedAt is when the most recent attempt ran. Useful for
	// "stuck since X" presentations in admin UIs.
	LastAttemptedAt time.Time `json:"last_attempted_at"`

	// NextRetryAfter is the earliest time the orchestrator should retry
	// this doc. The retry pre-pass picks rows where
	// next_retry_after <= now() AND terminal = false.
	NextRetryAfter time.Time `json:"next_retry_after"`

	// Terminal is true once the row has exceeded the configured maximum
	// attempt count. Excluded from automatic retry; remains in the table
	// for operator visibility.
	Terminal bool `json:"terminal"`

	// CreatedAt is when the first failure for this (source_id,
	// external_id) was recorded.
	CreatedAt time.Time `json:"created_at"`
}

SyncFailedDoc is the in-memory shape of one row in the sync_failed_documents skip-list table. Mirrors the schema 1:1; lives in domain so the driven port can reference it without importing postgres-specific types.

type SyncResult

type SyncResult struct {
	SourceID string    `json:"source_id"`
	Success  bool      `json:"success"`
	Skipped  bool      `json:"skipped,omitempty"` // true when another sync was already in progress for this source
	Stats    SyncStats `json:"stats"`
	Error    string    `json:"error,omitempty"`
	Duration float64   `json:"duration_seconds"`
	Cursor   string    `json:"cursor,omitempty"`
}

SyncResult represents the outcome of a sync operation

type SyncState

type SyncState struct {
	SourceID    string     `json:"source_id"`
	Status      SyncStatus `json:"status"`
	LastSyncAt  *time.Time `json:"last_sync_at,omitempty"`
	NextSyncAt  *time.Time `json:"next_sync_at,omitempty"`
	Cursor      string     `json:"cursor,omitempty"` // Pagination cursor for incremental sync
	Stats       SyncStats  `json:"stats"`
	Error       string     `json:"error,omitempty"`
	StartedAt   *time.Time `json:"started_at,omitempty"`
	CompletedAt *time.Time `json:"completed_at,omitempty"`
}

SyncState tracks the sync state for a source

type SyncStats

type SyncStats struct {
	DocumentsAdded   int `json:"documents_added"`
	DocumentsUpdated int `json:"documents_updated"`
	DocumentsDeleted int `json:"documents_deleted"`
	ChunksIndexed    int `json:"chunks_indexed"`
	Errors           int `json:"errors"`
}

SyncStats holds statistics for a sync operation

type SyncStatus

type SyncStatus string

SyncStatus represents the current state of a sync operation

const (
	SyncStatusIdle      SyncStatus = "idle"
	SyncStatusRunning   SyncStatus = "running"
	SyncStatusCompleted SyncStatus = "completed"
	SyncStatusFailed    SyncStatus = "failed"
)

type Task

type Task struct {
	// ID is the unique identifier for this task
	ID string `json:"id"`

	// Type identifies what kind of task this is
	Type TaskType `json:"type"`

	// TeamID is the team this task belongs to
	TeamID string `json:"team_id"`

	// Payload contains task-specific data
	// For sync_source: {"source_id": "src-123"}
	// For sync_container: {"source_id": "src-123", "container_id": "cnt-456"}
	// For sync_all: {} (empty)
	Payload map[string]string `json:"payload"`

	// Status is the current state of the task
	Status TaskStatus `json:"status"`

	// Priority determines processing order (higher = more urgent)
	// Default is 0, range is -100 to 100
	Priority int `json:"priority"`

	// Attempts is how many times this task has been attempted
	Attempts int `json:"attempts"`

	// MaxAttempts is the maximum retry count before giving up
	MaxAttempts int `json:"max_attempts"`

	// Error contains the last error message if failed
	Error string `json:"error,omitempty"`

	// CreatedAt is when the task was enqueued
	CreatedAt time.Time `json:"created_at"`

	// UpdatedAt is when the task was last modified
	UpdatedAt time.Time `json:"updated_at"`

	// StartedAt is when processing began (nil if not started)
	StartedAt *time.Time `json:"started_at,omitempty"`

	// CompletedAt is when processing finished (nil if not complete)
	CompletedAt *time.Time `json:"completed_at,omitempty"`

	// ScheduledFor is when the task should be processed (for delayed tasks)
	ScheduledFor time.Time `json:"scheduled_for"`
}

Task represents a background job to be processed by workers

func NewSyncAllTask

func NewSyncAllTask(teamID string) *Task

NewSyncAllTask creates a task to sync all sources for a team. Defaults to scheduled — only the periodic scheduler enqueues this variant today.

func NewSyncContainerTask added in v0.2.1

func NewSyncContainerTask(teamID, sourceID, containerID string) *Task

NewSyncContainerTask creates a task to sync a single container within a source. Defaults to manual because the only caller today is the admin path that adds a new container to an existing source — it's an explicit user action, not a scheduler tick.

func NewSyncSourceTask

func NewSyncSourceTask(teamID, sourceID string) *Task

NewSyncSourceTask creates a task to sync a specific source. The trigger defaults to TaskTriggerScheduled; callers that know better (the manual HTTP handler, the webhook receiver) should use NewSyncSourceTaskWithTrigger.

func NewSyncSourceTaskWithTrigger added in v0.4.0

func NewSyncSourceTaskWithTrigger(teamID, sourceID string, trigger TaskTrigger) *Task

NewSyncSourceTaskWithTrigger constructs a sync_source task with an explicit trigger label. Used by the admin "Sync now" button (manual) and by the future webhook receiver path.

func NewTask

func NewTask(taskType TaskType, teamID string, payload map[string]string) *Task

NewTask creates a new task with default values

func (*Task) CanRetry

func (t *Task) CanRetry() bool

CanRetry returns true if the task can be retried

func (*Task) ContainerID added in v0.2.1

func (t *Task) ContainerID() string

ContainerID extracts the container_id from the payload (for sync_container tasks)

func (*Task) IsReady

func (t *Task) IsReady() bool

IsReady returns true if the task is ready to be processed

func (*Task) MarkCompleted

func (t *Task) MarkCompleted()

MarkCompleted updates the task to completed state

func (*Task) MarkFailed

func (t *Task) MarkFailed(err string)

MarkFailed updates the task to failed state

func (*Task) MarkProcessing

func (t *Task) MarkProcessing()

MarkProcessing updates the task to processing state

func (*Task) Retry

func (t *Task) Retry(err string)

Retry resets the task for retry with exponential backoff

func (*Task) SourceID

func (t *Task) SourceID() string

SourceID extracts the source_id from the payload (for sync_source and sync_container tasks)

func (*Task) Trigger added in v0.4.0

func (t *Task) Trigger() TaskTrigger

Trigger returns the TaskTrigger recorded on the payload, or TaskTriggerScheduled when absent / unrecognised. The fallback matches the previous implicit default so any task enqueued before the trigger field existed is treated as a scheduled sync.

type TaskResult

type TaskResult struct {
	TaskID      string        `json:"task_id"`
	Success     bool          `json:"success"`
	Error       string        `json:"error,omitempty"`
	Duration    time.Duration `json:"duration"`
	ItemsCount  int           `json:"items_count,omitempty"`  // e.g., documents synced
	ErrorsCount int           `json:"errors_count,omitempty"` // e.g., documents failed
}

TaskResult represents the outcome of processing a task

type TaskStatus

type TaskStatus string

TaskStatus represents the current state of a task

const (
	TaskStatusPending    TaskStatus = "pending"
	TaskStatusProcessing TaskStatus = "processing"
	TaskStatusCompleted  TaskStatus = "completed"
	TaskStatusFailed     TaskStatus = "failed"
)

type TaskTrigger added in v0.4.0

type TaskTrigger string

TaskTrigger identifies why a sync task was enqueued. Carried on the task payload so the worker can thread it into the orchestrator's context; downstream observers can read it back via SyncTriggerFromContext to label sync events accurately.

"scheduled" is the historical default and what callers get if the payload key is missing — preserves backward compat for any tasks that were enqueued before the field existed.

const (
	// TaskTriggerManual — a user clicked "Sync now" in the admin UI.
	TaskTriggerManual TaskTrigger = "manual"
	// TaskTriggerScheduled — the periodic scheduler tick enqueued the task.
	TaskTriggerScheduled TaskTrigger = "scheduled"
	// TaskTriggerWebhook — an external push notification (e.g. Microsoft
	// Graph driveItem webhook) triggered the sync. Reserved for future
	// wiring; no caller sets this today.
	TaskTriggerWebhook TaskTrigger = "webhook"
)

type TaskType

type TaskType string

TaskType identifies the type of background task

const (
	// TaskTypeSyncSource syncs a specific source
	TaskTypeSyncSource TaskType = "sync_source"
	// TaskTypeSyncAll syncs all sources for a team
	TaskTypeSyncAll TaskType = "sync_all"
	// TaskTypeSyncContainer syncs a single container within a source
	TaskTypeSyncContainer TaskType = "sync_container"
)

type Team

type Team struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

Team represents an organization/team

type TokenClaims

type TokenClaims struct {
	UserID    string `json:"user_id"`
	Email     string `json:"email"`
	Role      Role   `json:"role"`
	TeamID    string `json:"team_id"`
	SessionID string `json:"session_id"`
	IssuedAt  int64  `json:"iat"`
	ExpiresAt int64  `json:"exp"`
}

TokenClaims represents the JWT token payload

type TokenRequest added in v0.2.1

type TokenRequest struct {
	GrantType    OAuthGrantType `json:"grant_type"`
	Code         string         `json:"code,omitempty"`         // For authorization_code grant
	RedirectURI  string         `json:"redirect_uri,omitempty"` // For authorization_code grant
	ClientID     string         `json:"client_id"`
	ClientSecret string         `json:"client_secret,omitempty"` // For confidential clients
	CodeVerifier string         `json:"code_verifier,omitempty"` // PKCE verifier
	RefreshToken string         `json:"refresh_token,omitempty"` // For refresh_token grant
	Resource     string         `json:"resource,omitempty"`      // RFC 8707 resource indicator
}

TokenRequest represents an OAuth 2.0 token request

type TokenResponse added in v0.2.1

type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`              // "Bearer"
	ExpiresIn    int64  `json:"expires_in"`              // Seconds until expiration
	RefreshToken string `json:"refresh_token,omitempty"` // Optional refresh token
	Scope        string `json:"scope,omitempty"`         // Space-delimited scopes
}

TokenResponse represents an OAuth 2.0 token response

type TokenUsage added in v0.2.2

type TokenUsage struct {
	// PromptTokens is the number of tokens in the input prompt
	PromptTokens int `json:"prompt_tokens"`

	// CompletionTokens is the number of tokens in the generated response
	CompletionTokens int `json:"completion_tokens"`

	// TotalTokens is the sum of prompt and completion tokens
	TotalTokens int `json:"total_tokens"`
}

TokenUsage tracks token consumption for an LLM request. This is useful for cost tracking and rate limiting.

type UpcomingJobs

type UpcomingJobs struct {
	// PendingTasks are tasks ready to be processed now
	PendingTasks []*Task `json:"pending_tasks"`

	// ScheduledTasks are recurring task schedules
	ScheduledTasks []*ScheduledTask `json:"scheduled_tasks"`

	// NextScheduledRun is when the next scheduled task will run
	NextScheduledRun *time.Time `json:"next_scheduled_run,omitempty"`
}

UpcomingJobs represents pending and scheduled jobs This is a value object combining different job types for the upcoming jobs view

func NewUpcomingJobs

func NewUpcomingJobs(pendingTasks []*Task, scheduledTasks []*ScheduledTask) *UpcomingJobs

NewUpcomingJobs creates a new upcoming jobs value object

type User

type User struct {
	ID           string     `json:"id"`
	Email        string     `json:"email"`
	PasswordHash string     `json:"-"` // Never serialize
	Name         string     `json:"name"`
	Role         Role       `json:"role"`
	TeamID       string     `json:"team_id"`
	Active       bool       `json:"active"`
	CreatedAt    time.Time  `json:"created_at"`
	UpdatedAt    time.Time  `json:"updated_at"`
	LastLoginAt  *time.Time `json:"last_login_at,omitempty"`
}

User represents a team member

func (*User) CanManageSources

func (u *User) CanManageSources() bool

CanManageSources checks if the user can create/delete sources

func (*User) CanManageUsers

func (u *User) CanManageUsers() bool

CanManageUsers checks if the user can create/delete other users

func (*User) CanSearch

func (u *User) CanSearch() bool

CanSearch checks if the user can perform searches

func (*User) IsAdmin

func (u *User) IsAdmin() bool

IsAdmin checks if the user has admin privileges

func (*User) ToSummary

func (u *User) ToSummary() *UserSummary

ToSummary converts a User to UserSummary

type UserSummary

type UserSummary struct {
	ID          string     `json:"id"`
	Email       string     `json:"email"`
	Name        string     `json:"name"`
	Role        Role       `json:"role"`
	Active      bool       `json:"active"`
	LastLoginAt *time.Time `json:"last_login_at,omitempty"`
}

UserSummary provides a safe view of user data (no password hash)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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