search

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package search is the unified search service: a SQLite FTS5 index fed by providers, filtered through an Authorizer before any hit leaves it.

Index

Constants

View Source
const (
	DefaultMaxAge = 60 * time.Second

	DefaultPageSize = 20
	MaxPageSize     = 100
)

Defaults for the freshness policy.

Variables

View Source
var Actions = []ActionDef{
	{ID: "app.deploy", Title: "Deploy", Keywords: "deploy release ship redeploy", Scope: ScopeApp, Ability: abilityDeploy, Confirm: true, Href: "/apps/{name}?action=deploy", MCPTool: "deploy_app", MCPNeeds: []string{"image"}},
	{ID: "app.restart", Title: "Restart", Keywords: "restart reboot bounce", Scope: ScopeApp, Ability: abilityDeploy, Confirm: true, Href: "/apps/{name}?action=restart", MCPTool: "restart_app"},
	{ID: "app.logs", Title: "Open logs for", Keywords: "logs tail output", Scope: ScopeApp, Ability: abilityRead, Href: "/apps/{name}/logs", MCPTool: "get_app_logs"},
	{ID: "app.rollback", Title: "Rollback", Keywords: "rollback revert previous", Scope: ScopeApp, Ability: abilityDeploy, Confirm: true, Href: "/apps/{name}/deploys?action=rollback", MCPTool: "rollback_app_to_deploy", MCPNeeds: []string{"deploy_id"}},
	{ID: "app.stop", Title: "Stop", Keywords: "stop halt pause", Scope: ScopeApp, Ability: abilityDeploy, Confirm: true, Href: "/apps/{name}?action=stop"},
	{ID: "app.add-domain", Title: "Add a domain to", Keywords: "add domain hostname dns", Scope: ScopeApp, Ability: abilityWrite, Href: "/apps/{name}/domains", MCPTool: "set_app_domains", MCPNeeds: []string{"domains"}},
	{ID: "db.backup-now", Title: "Back up now", Keywords: "backup now snapshot dump", Scope: ScopeDatabase, Ability: abilityWriteSensitive, Confirm: true, Href: "/databases/{name}?action=backup"},
	{ID: "global.new-app", Title: "Create a new app", Keywords: "new app create deploy", Scope: ScopeGlobal, Ability: abilityWrite, Href: "/apps/new"},
	{ID: "global.add-node", Title: "Add a node", Keywords: "add node server enroll join", Scope: ScopeGlobal, Ability: abilityRoot, Href: "/nodes"},
}

Actions is the registry. Href may contain {name}.

View Source
var ErrInvalid = errors.New("search: invalid request")

ErrInvalid marks a rejected request.

View Source
var ErrNotFound = errors.New("search: not found")

ErrNotFound is returned for a ref the caller may not see or that is gone.

Functions

This section is empty.

Types

type ActionDef

type ActionDef struct {
	ID       string
	Title    string
	Keywords string
	Scope    Scope
	Ability  string
	Confirm  bool
	Href     string
	// MCPTool is the MCP tool that performs the action; empty when none exists.
	MCPTool string
	// MCPNeeds are inputs the agent must supply on top of the target name.
	MCPNeeds []string
}

ActionDef is one entry of the action registry. Ability and Confirm mirror the route the action calls, so a hit never offers more than the API allows.

type ActionInfo

type ActionInfo struct {
	ID      string `json:"id"`
	Ability string `json:"ability"`
	Confirm bool   `json:"confirm"`
	// MCPTool, MCPArgs and MCPNeeds say which MCP tool runs this action, with
	// which arguments filled in, and which inputs the agent must still supply.
	MCPTool  string            `json:"mcp_tool,omitempty"`
	MCPArgs  map[string]string `json:"mcp_args,omitempty"`
	MCPNeeds []string          `json:"mcp_needs,omitempty"`
}

ActionInfo is what a client needs to run an action hit.

type ActionsProvider

type ActionsProvider struct {
	AppNames      func(ctx context.Context) ([]string, error)
	DatabaseNames func(ctx context.Context) ([]string, error)
}

ActionsProvider emits the registry once per target resource.

func (ActionsProvider) Docs

func (p ActionsProvider) Docs(ctx context.Context) ([]Doc, error)

Docs implements Provider.

func (ActionsProvider) Kind

func (ActionsProvider) Kind() Kind

Kind implements Provider.

type Authorizer

type Authorizer interface {
	Allowed(prefix, name, ability string) bool
}

Authorizer answers whether the caller may see a resource with an ability. The API layer implements it with the same engine the path routes use.

type Caller

type Caller struct {
	PrincipalID string
	Authz       Authorizer
}

Caller is who is searching.

type DB

type DB interface {
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
	BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
}

DB is the slice of *sql.DB the service uses.

type Doc

type Doc struct {
	Ref       string
	Kind      Kind
	Title     string
	Body      string
	Tags      []string
	App       string
	Env       string
	Node      string
	Prefix    string
	Name      string
	Ability   string
	Confirm   bool
	Href      string
	ActionID  string
	UpdatedAt time.Time
}

Doc is one indexed thing. Prefix and Name name the resource whose visibility decides whether the doc may be returned, Ability is what the caller must hold on it.

func (Doc) Validate

func (d Doc) Validate() error

Validate rejects a doc that would bypass the authorization hook.

type Hit

type Hit struct {
	Ref        string      `json:"ref"`
	Kind       Kind        `json:"kind"`
	Title      string      `json:"title"`
	Subtitle   string      `json:"subtitle,omitempty"`
	AppName    string      `json:"app_name,omitempty"`
	Env        string      `json:"env,omitempty"`
	NodeName   string      `json:"node_name,omitempty"`
	Tags       []string    `json:"tags,omitempty"`
	Href       string      `json:"href,omitempty"`
	TitleSpans []Span      `json:"title_spans,omitempty"`
	Score      float64     `json:"score"`
	Action     *ActionInfo `json:"action,omitempty"`
}

Hit is one search result.

type Kind

type Kind string

Kind is the category of a search document.

const (
	KindApp        Kind = "app"
	KindDatabase   Kind = "database"
	KindNode       Kind = "node"
	KindDomain     Kind = "domain"
	KindTemplate   Kind = "template"
	KindProject    Kind = "project"
	KindEnv        Kind = "environment"
	KindTag        Kind = "tag"
	KindDeployment Kind = "deployment"
	KindSetting    Kind = "setting"
	KindDoc        Kind = "doc"
	KindAction     Kind = "action"
	KindSaved      Kind = "saved"
)

Document kinds.

func ParseKind

func ParseKind(s string) (Kind, bool)

ParseKind resolves a kind name or alias.

type Provider

type Provider interface {
	Kind() Kind
	Docs(ctx context.Context) ([]Doc, error)
}

Provider produces every current doc of one kind.

type Query

type Query struct {
	Text []string
	App  string
	Env  string
	Tag  string
	Node string
	Kind Kind
}

Query is a parsed search string: scoped filters plus free text.

func ParseQuery

func ParseQuery(q string) Query

ParseQuery splits `app:foo env:prod kind:db restart api` into scopes and free text. An unknown `key:value` stays free text, so URLs and image names remain searchable.

func (Query) Empty

func (q Query) Empty() bool

Empty reports whether the query has neither text nor a scope.

type Request

type Request struct {
	Q      string
	Kind   Kind
	Limit  int
	Cursor string
}

Request is one search call.

type Response

type Response struct {
	Hits       []Hit  `json:"hits"`
	NextCursor string `json:"next_cursor,omitempty"`
	Total      int    `json:"total"`
}

Response is a page of authorized hits.

type Saved

type Saved struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	Query     string    `json:"query"`
	CreatedAt time.Time `json:"created_at"`
}

Saved is a stored query of one principal.

type Scope

type Scope string

Scope says what an action targets.

const (
	ScopeApp      Scope = "app"
	ScopeDatabase Scope = "database"
	ScopeGlobal   Scope = "global"
)

Action scopes.

type Service

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

Service owns the index and answers queries.

func New

func New(db DB, logger *slog.Logger, providers ...Provider) *Service

New builds a Service over db and providers.

func (*Service) DeleteSaved

func (s *Service) DeleteSaved(ctx context.Context, principal, id string) error

DeleteSaved removes one of the principal's saved searches.

func (*Service) ListSaved

func (s *Service) ListSaved(ctx context.Context, principal string) ([]Saved, error)

ListSaved returns the principal's saved searches, newest first.

func (*Service) MarkDirty

func (s *Service) MarkDirty()

MarkDirty records that a store changed; the next query reconciles.

func (*Service) Providers

func (s *Service) Providers() []Provider

Providers lists the registered providers.

func (*Service) RankedNames

func (s *Service) RankedNames(ctx context.Context, c Caller, q string, kind Kind) ([]string, bool, error)

RankedNames returns the names of every authorized doc of kind matching q in rank order, and false when the index has never been built. It filters through the same Authorizer as Search.

func (*Service) Reconcile

func (s *Service) Reconcile(ctx context.Context) error

Reconcile re-reads every provider into the index.

func (*Service) RecordUse

func (s *Service) RecordUse(ctx context.Context, c Caller, ref string) error

RecordUse bumps the caller's frequency for a hit they opened. A ref the caller may not see is reported as not found.

func (*Service) Reindex

func (s *Service) Reindex(ctx context.Context, kind Kind) error

Reindex re-reads one kind, the hook a store change calls.

func (*Service) SaveSearch

func (s *Service) SaveSearch(ctx context.Context, principal, name, query string) (Saved, error)

SaveSearch stores or replaces a named query for the principal.

func (*Service) Search

func (s *Service) Search(ctx context.Context, c Caller, req Request) (Response, error)

Search runs the query, drops every doc the caller's Authorizer refuses, then ranks what is left.

func (*Service) Suggest

func (s *Service) Suggest(ctx context.Context, c Caller, q string) ([]Suggestion, error)

Suggest completes scope keys and returns the top authorized titles.

type Span

type Span struct {
	Start int `json:"start"`
	End   int `json:"end"`
}

Span is a half-open range of Unicode code points in a hit's title.

type Suggestion

type Suggestion struct {
	Text string `json:"text"`
	Kind string `json:"kind"`
	Ref  string `json:"ref,omitempty"`
}

Suggestion is one autocomplete entry.

Jump to

Keyboard shortcuts

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