daemon

package
v0.104.0 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package daemon owns the one local, read-only `datatug serve` process that AI-agent mods use so their cards can link to the DataTug web UI (spec/features/cli/daemon). The CLI is the owner of that process: it records it in a state directory (pidfile, record, log) and stops it with Stop.

Index

Constants

View Source
const (
	// DefaultPort is the preferred agent port; MaxPort bounds the search for a free one.
	DefaultPort = 8989
	MaxPort     = 9009
	// Host is the only address the daemon binds.
	Host = "127.0.0.1"
	// IdleTimeout is how long the daemon runs without a request before it exits.
	IdleTimeout = 2 * time.Hour

	// Fixed failure phrases: they never carry a path, URL or credential.
	ErrPhraseNoPort   = "no free port between 8989 and 9009"
	ErrPhraseExited   = "server exited during start"
	ErrPhraseTimeout  = "server was not ready within 5s"
	ErrPhraseNoBinary = "cannot locate the datatug binary"
	ErrPhraseSpawn    = "cannot launch the server"
	ErrPhraseState    = "cannot write the daemon state directory"
	// ErrPhrasePolicyPrincipal accompanies StartPolicyPrincipalRequired: a policy set exists but
	// the daemon has no principal to serve as. It never carries the policy path or contents.
	ErrPhrasePolicyPrincipal = "a policy set exists but no principal was named for the daemon"

	// StartPolicyPrincipalRequired is the stable machine-readable Status.StartError value.
	StartPolicyPrincipalRequired = "policy-principal-required"
)
View Source
const (
	KindTableMetadata = "table-metadata"
	KindTableRecords  = "table-records"
	KindRecord        = "record"

	// UISupport values: pending means the web UI has no route for the kind yet.
	UISupportAvailable = "available"
	UISupportPending   = "pending"

	// DefaultUIBase is the hosted web UI; DATATUG_UI_BASE replaces it.
	DefaultUIBase = "https://datatug.app"
	UIBaseEnv     = "DATATUG_UI_BASE"

	DefaultLimit = 50
	MaxLimit     = 1000
)

Link kinds, as printed by `daemon url`.

View Source
const ContractVersion = 1

ContractVersion is the shape version of the `daemon url` JSON: bumped on any incompatible change.

View Source
const DirEnv = "DATATUG_DAEMON_DIR"

DirEnv overrides the state directory (default ~/datatug/daemon).

View Source
const KindQueryResult = "query-result"

KindQueryResult is the link of a stored query result (`daemon url --result`).

View Source
const (
	// MaxLinkLength bounds a link: the mod SDK draws at most this many characters.
	MaxLinkLength = 2048
)
View Source
const MaxResultRow = 10000

MaxResultRow bounds --row: a position among the rows a result keeps.

View Source
const WarningKeyNotScanned = "primary key not in scanned metadata: key not validated"

WarningKeyNotScanned is the warning of a record link to a table whose scan holds no primary-key information: the key is not validated.

View Source
const WarningNotScanned = "table metadata not scanned: link not validated"

WarningNotScanned is the one warning of a link that was not checked against the project's scanned metadata: the project has none for the source (never scanned, or it cannot be read).

Variables

View Source
var ErrNotOwned = errors.New("no daemon started by this CLI")

ErrNotOwned is returned by Stop for a datatug API this CLI did not start.

View Source
var ResultViews = []string{"table", "record", "columns", "sources", "related"}

ResultViews are the views of a result page, in the order the card shows them.

Functions

func UIBase

func UIBase(port int) string

UIBase is the web UI address of the agent on port.

Types

type ErrNotRunning

type ErrNotRunning struct{ Phrase string }

ErrNotRunning is returned by Start, next to its Status, when no server runs.

func (ErrNotRunning) Error

func (e ErrNotRunning) Error() string
type Link struct {
	Contract  int    `json:"contract"`
	URL       string `json:"url"`
	Kind      string `json:"kind"`
	Running   bool   `json:"running"`
	UISupport string `json:"uiSupport"`
	// Warnings is always present ([] when empty) so the shape is stable.
	Warnings []string `json:"warnings"`
}

Link is the deep link of one card.

func BuildLink(st Status, req LinkRequest) (Link, error)

BuildLink builds the deep link of contract deep-link-contract for the agent reported by st: identity in the path, view parameters (limit, schema) in the query, the record key in the fragment so it is never sent to the web host. It never reaches the network.

type LinkRequest

type LinkRequest struct {
	Project, Env, DB, Table, Schema string
	View                            bool
	Records                         bool
	Limit                           int
	Record                          string // primary key as a JSON object
	// Code is the one-time code of the agent (agentaccess.MintCode), put first in the fragment of
	// every link; "" when the agent has none to give.
	Code string
	// Metadata reads the scanned relations named Table (nil when the project holds none): the link is
	// checked against them, and carries a warning when there is nothing to check against. It runs
	// last, after every check of the request itself; a nil function is no metadata.
	Metadata func() *Metadata
}

LinkRequest names one card's target.

type Manager

type Manager struct {
	Dir   string
	Exe   func() (string, error)
	Probe func(port int) (version string, ok bool)
	// Dial says whether something accepts a TCP connection on 127.0.0.1:port. It sends no
	// HTTP request, so it never counts as activity for the server's idle timer.
	Dial     func(port int) bool
	PortFree func(port int) bool
	Alive    func(pid int) bool
	// Owns says whether pid is a `datatug serve` on port (guards against a reused pid).
	Owns      func(pid, port int) bool
	Terminate func(pid int) error
	// Spawn starts the detached server and returns its pid and a channel closed when it exits.
	Spawn func(exe string, args []string, logPath string) (pid int, exited <-chan struct{}, err error)
	Now   func() time.Time
	Sleep func(time.Duration)
	// PolicySetPresent says whether `serve` would find a policy set and so refuse to start
	// without a named principal; nil means no. It reads nothing beyond existence/validity.
	PolicySetPresent func() bool
	// StartWait bounds the readiness wait; Poll is its interval.
	StartWait time.Duration
	Poll      time.Duration
}

Manager holds the state directory and every seam over the OS, so tests drive the whole lifecycle without a real process or port.

func NewManager

func NewManager() *Manager

NewManager returns the Manager wired to the real OS.

func (*Manager) LogFile

func (m *Manager) LogFile() string

LogFile is where the daemon's stdout and stderr go.

func (*Manager) Start

func (m *Manager) Start(caller string) (Status, error)

Start ensures the daemon runs. The returned Status is always set; the error is ErrNotRunning when no server could be brought up.

func (*Manager) Status

func (m *Manager) Status() Status

Status reports the daemon without starting or stopping anything, and without sending an HTTP request to a server this CLI started (a request would reset its idle timer): the record, the pid and a TCP connect decide (no process-command check, which can fail in a sandbox and, on Windows, is an HTTP probe). A datatug API this CLI did not start (no record of it) is the one case that costs a single agent-info request, because a TCP connect cannot tell it from a foreign listener on the port.

func (*Manager) Stop

func (m *Manager) Stop() error

Stop ends the daemon this CLI started. A datatug API it did not start is never touched (ErrNotOwned); any other process is never signalled.

type Metadata added in v0.92.0

type Metadata struct{ Relations []Relation }

Metadata is the scanned relations of one source, as the project declares them. A nil *Metadata means the project holds none (the source was never scanned); relations are those of the one name asked for, so an empty one is a scanned source without that name.

type NotFoundError added in v0.92.0

type NotFoundError struct{ Msg string }

NotFoundError is a link to a table or view that the project's scanned metadata does not list (exit code 3). It names the kind of problem only, never the name asked for.

func (NotFoundError) Error added in v0.92.0

func (e NotFoundError) Error() string

type Record

type Record struct {
	PID         int       `json:"pid"`
	Port        int       `json:"port"`
	URL         string    `json:"url"`
	StartedAt   time.Time `json:"startedAt"`
	StartedBy   string    `json:"startedBy"`
	Version     string    `json:"version"`
	Binary      string    `json:"binary"`
	IdleTimeout string    `json:"idleTimeout"`
}

Record is what the CLI persists about the daemon it started (daemon.json).

type Relation added in v0.92.0

type Relation struct {
	Schema, Name string
	View         bool
	PrimaryKey   []string
}

Relation is one scanned table or view: its schema ("" for a schemaless source), its name and, for a table, its primary-key column names (none when the scan holds no key information).

type ResultLink struct {
	Contract     int    `json:"contract"`
	URL          string `json:"url,omitempty"`
	Kind         string `json:"kind"`
	Running      bool   `json:"running"`
	UISupport    string `json:"uiSupport"`
	ResultExists bool   `json:"resultExists"`
	// Warnings is always present ([] when empty), as on every daemon url link.
	Warnings []string `json:"warnings"`
}

ResultLink is the `daemon url --result` answer: the link is absent when the result is not there.

func BuildResultLink(st Status, req ResultLinkRequest, validID func(string) bool) (ResultLink, error)

BuildResultLink builds <uiBase>/result/<id>?view=<view>[&row=<n>]#code=<code>. The link carries no project, database, table, column, value or query text: the page reads all of it from the agent. validID is the result id grammar. It never reaches the network.

type ResultLinkRequest added in v0.94.0

type ResultLinkRequest struct {
	ID   string
	View string
	Row  int
	// Exists is whether the result's meta.json is there; no link is built
	// for a result that is not.
	Exists bool
	// Code is the one-time code put first in the fragment; "" for none.
	Code string
}

ResultLinkRequest names a stored result, the view to open and, optionally, the position of a row (never a key).

type Status

type Status struct {
	Running     bool   `json:"running"`
	URL         string `json:"url"`
	UIBase      string `json:"uiBase"`
	Port        int    `json:"port"`
	PID         int    `json:"pid"`
	StartedBy   string `json:"startedBy"`
	StartedAt   string `json:"startedAt,omitempty"`
	Version     string `json:"version"`
	LogFile     string `json:"logFile"`
	IdleTimeout string `json:"idleTimeout"`
	Error       string `json:"error,omitempty"`
	// StartError is the stable machine-readable reason a start failed, when one is named
	// (StartPolicyPrincipalRequired); omitted when empty.
	StartError string `json:"startError,omitempty"`
}

Status is the machine-readable state printed by `daemon status|start`.

type UsageError

type UsageError struct{ Msg string }

UsageError is a bad `daemon url` input (exit code 2).

func (UsageError) Error

func (e UsageError) Error() string

Jump to

Keyboard shortcuts

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