daemon

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: May 11, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package daemon implements a long-lived krit process that keeps parse trees, the cross-file index, oracle state, and typeinfer caches resident in memory. CLI verbs in the build-integration cluster prefer the daemon when a socket is reachable and fall back to in-process execution otherwise.

The protocol is line-delimited JSON over a Unix socket. Each request is a single JSON object terminated by a newline; each response is a single JSON object terminated by a newline.

Index

Constants

View Source
const (
	VerbStatus         = "status"
	VerbShutdown       = "shutdown"
	VerbAbiHash        = "abi-hash"
	VerbAnalyzeBuffer  = "analyze-buffer"
	VerbAnalyzeBuffers = "analyze-buffers"
	VerbAnalyzeProject = "analyze-project"
)

Built-in verb names.

Variables

This section is empty.

Functions

func Available

func Available(socketPath string) bool

Available reports whether a daemon appears reachable at socketPath. It performs a short-timeout dial; absence is not an error from the caller's perspective, just "fall back to in-process".

func Call

func Call(socketPath, verb string, args any, out any) error

Call sends a single Request to the daemon at socketPath and decodes the Data field of the Response into out (which may be nil). It returns the daemon's error string as a Go error when OK=false.

func DefaultSocketPath

func DefaultSocketPath(root string) string

DefaultSocketPath returns the conventional socket path for a project rooted at root (.krit/daemon.sock under the project root).

func WriteErrorResponseLine added in v0.2.0

func WriteErrorResponseLine(w io.Writer, msg string) error

WriteErrorResponseLine emits a `{"ok":false,"error":...}\n` line using the daemon's wire format. Exported so RawResponseWriter implementations can fall back to the standard error envelope without re-implementing the line-delimited shape.

Types

type AbiHashArgs

type AbiHashArgs struct {
	Target string `json:"target"`
}

AbiHashArgs is the argument shape for the abi-hash verb.

type AbiHashResult

type AbiHashResult struct {
	Target string `json:"target"`
	Module string `json:"module,omitempty"`
	File   string `json:"file,omitempty"`
	Hash   string `json:"hash"`
	Inputs int    `json:"inputs"`
}

AbiHashResult is the response payload for the abi-hash verb.

type AnalyzeBufferArgs

type AnalyzeBufferArgs struct {
	// Path is the filesystem path the buffer represents. Used as the
	// cache key and as the file label in findings. Empty paths default
	// to "input.kt".
	Path string `json:"path"`
	// Content is the buffer body. UTF-8 Kotlin source.
	Content string `json:"content"`
}

AnalyzeBufferArgs carries an in-memory file body for single-buffer per-file rule dispatch. The daemon parses the buffer (or reuses a cached parse when content is identical) and runs the same per-file rule pass the LSP / MCP single-file paths use.

type AnalyzeBufferEntry

type AnalyzeBufferEntry struct {
	Findings json.RawMessage `json:"findings,omitempty"`
	CacheHit bool            `json:"cache_hit"`
	Error    string          `json:"error,omitempty"`
}

AnalyzeBufferEntry is one slot in AnalyzeBuffersResult.Results.

type AnalyzeBufferResult

type AnalyzeBufferResult struct {
	Findings json.RawMessage `json:"findings"`
	CacheHit bool            `json:"cache_hit"`
}

AnalyzeBufferResult carries the per-file findings for an analyze-buffer call. Findings is the canonical JSON form of scanner.FindingColumns so the wire shape matches `krit -f json` output. CacheHit is true when the daemon's WorkspaceState served this buffer from a prior parse.

type AnalyzeBuffersArgs

type AnalyzeBuffersArgs struct {
	Buffers []AnalyzeBufferArgs `json:"buffers"`
}

AnalyzeBuffersArgs is the batched form of AnalyzeBufferArgs. The daemon processes Buffers in order and returns one result per buffer. Clients with N staged files trade N dial+RTT cycles for one.

type AnalyzeBuffersResult

type AnalyzeBuffersResult struct {
	Results []AnalyzeBufferEntry `json:"results"`
}

AnalyzeBuffersResult mirrors AnalyzeBuffersArgs: one result per input buffer in matching order. A buffer-level error (e.g. parse failure) populates Error and leaves Findings empty for that entry, so the caller still gets dispositive results for the rest of the batch instead of one bad file failing the whole call.

type AnalyzeProjectArgs added in v0.2.0

type AnalyzeProjectArgs struct {
	// Paths is the explicit scan target. Empty means "use the
	// daemon's --root".
	Paths []string `json:"paths,omitempty"`
	// Format is the output format. Empty defaults to "json".
	Format string `json:"format,omitempty"`
	// BaselinePath, when non-empty, points at a baseline file used
	// to suppress known findings.
	BaselinePath string `json:"baseline,omitempty"`
	// DiffRef, when non-empty, restricts findings to files changed
	// since the given git ref.
	DiffRef string `json:"diff,omitempty"`
	// MinConfidence drops findings below the threshold from output.
	MinConfidence float64 `json:"min_confidence,omitempty"`
	// WarningsAsErrors promotes warning-severity findings to errors
	// before format dispatch.
	WarningsAsErrors bool `json:"warnings_as_errors,omitempty"`
	// IncludeGenerated retains files under */generated/* during parse.
	IncludeGenerated bool `json:"include_generated,omitempty"`
	// AllRules opts into experimental rules in addition to the
	// default core set.
	AllRules bool `json:"all_rules,omitempty"`
	// Experimental enables experimental flags whose default-off behavior
	// the daemon would otherwise suppress.
	Experimental bool `json:"experimental,omitempty"`
	// EnableRules / DisableRules are comma-separated rule-id lists.
	EnableRules  string `json:"enable_rules,omitempty"`
	DisableRules string `json:"disable_rules,omitempty"`
	// RequireWarm, when true, makes the verb fail fast (with a typed
	// error) instead of paying cold-warm cost. Clients that want a
	// hard SLA set this — useful for IDE workflows that can show a
	// "warming up" indicator instead of blocking on the first call.
	RequireWarm bool `json:"require_warm,omitempty"`
}

AnalyzeProjectArgs drives the analyze-project verb. The verb runs the same whole-project scan pipeline as `krit -f json` against the daemon's resident parse cache (and, in future commits, resident resolver and oracle), so the JSON shape returned in AnalyzeProjectResult.Findings matches the CLI output byte-for-byte modulo timing fields.

Empty fields take daemon defaults; fields mirror the most useful CLI flags one-to-one so client wrappers can translate flag-by-flag.

type AnalyzeProjectResult added in v0.2.0

type AnalyzeProjectResult struct {
	Findings json.RawMessage     `json:"findings"`
	Stats    AnalyzeProjectStats `json:"stats"`
}

AnalyzeProjectResult is the response payload. Findings is the raw bytes the OutputPhase formatter wrote, so callers can `json.Unmarshal(res.Findings, &theirSchema)` with no shape change from `krit -f json`.

type AnalyzeProjectStats added in v0.2.0

type AnalyzeProjectStats struct {
	FilesScanned    int     `json:"files_scanned"`
	FindingsCount   int     `json:"findings_count"`
	WallSeconds     float64 `json:"wall_seconds"`
	CodeIndexHit    bool    `json:"code_index_hit"`
	LibraryFactsHit bool    `json:"library_facts_hit"`
	// ResolverHit reports whether the resident type-resolver slot was
	// consulted and served (cached pointer reused). Mirrors
	// CodeIndexHit semantics — true means the slot was populated when
	// the verb ran; the run may have rebuilt mid-verb on fingerprint
	// mismatch.
	ResolverHit bool `json:"resolver_hit"`
	// OracleFilterHit reports whether the resident oracle call-filter
	// slot was populated at verb entry. Useful for confirming that
	// PR-C's filter cache is warm after the first oracle-enabled call.
	OracleFilterHit bool `json:"oracle_filter_hit"`
	// DirtyFiles is the count of files Touched in WorkspaceState
	// since the last analyze-project call (drained at the start of
	// this call). Useful for clients that want to show "N files
	// changed since last scan" UX.
	DirtyFiles int `json:"dirty_files"`
	// Cold is true on the first analyze-project call after daemon
	// startup; subsequent calls report false.
	Cold bool `json:"cold"`
	// ParseHits and ParseMisses are the per-call delta against the
	// resident parse cache (combined Kotlin + Java). Both stay 0 when
	// no parse cache is attached.
	ParseHits   int64 `json:"parse_hits"`
	ParseMisses int64 `json:"parse_misses"`
	// FindingsBundleHit is true when the whole-run findings cache
	// served the result without redoing dispatch or cross-file work.
	// A true value means the call was a structural reuse of a prior
	// identical-input run.
	FindingsBundleHit bool `json:"findings_bundle_hit"`
	// PhaseTimingsMs is the per-phase wall-time breakdown for this
	// call. Phases skipped on a findings-bundle hit (dispatch,
	// crossfile, android) report 0. Useful for diagnosing which phase
	// dominates a slow warm call without a full pprof capture.
	PhaseTimingsMs PhaseTimingsMs `json:"phase_timings_ms"`
}

AnalyzeProjectStats reports per-call observability data — useful for clients that want to log warm-vs-cold cadence, or for tests asserting that the dirty-set behaves as expected after a single- file change.

type Handler

type Handler func(ctx context.Context, args json.RawMessage) (any, error)

Handler runs a single verb. It receives the raw JSON args and returns a JSON-marshalable result or an error. Handlers may be invoked concurrently; the verb implementation is responsible for any internal locking.

A handler may return a value that implements RawResponseWriter to skip the default json.Marshal envelope and stream the response directly into the connection. This is the streaming path issue #60 added so the analyze-project verb avoids buffering the multi-megabyte findings JSON in memory.

type PhaseTimingsMs added in v0.2.0

type PhaseTimingsMs struct {
	Parse     int64 `json:"parse"`
	Index     int64 `json:"index"`
	Dispatch  int64 `json:"dispatch"`
	CrossFile int64 `json:"crossfile"`
	Android   int64 `json:"android"`
	Fixup     int64 `json:"fixup"`
	Output    int64 `json:"output"`
}

PhaseTimingsMs mirrors pipeline.PhaseTimingsMs on the wire so daemon clients can introspect per-phase cost without a separate fetch. All values are wall-clock milliseconds.

type RawResponseWriter added in v0.2.0

type RawResponseWriter interface {
	WriteRawResponse(ctx context.Context, w io.Writer) error
}

RawResponseWriter is the optional interface a Handler result can implement to bypass the json.Marshal + Response envelope path. The dispatch loop hands the request context plus connection writer; the implementation must write a complete newline-terminated Response envelope. WriteResponseLine is the canonical helper for the fallback/error case.

type Request

type Request struct {
	Verb string          `json:"verb"`
	Args json.RawMessage `json:"args,omitempty"`
}

Request names a verb and carries its arguments as opaque JSON.

type Response

type Response struct {
	OK    bool            `json:"ok"`
	Error string          `json:"error,omitempty"`
	Data  json.RawMessage `json:"data,omitempty"`
}

Response is the wire form of a verb result. OK=false carries an Error message and an empty Data; OK=true carries the verb-specific Data.

type Server

type Server struct {

	// Reporter, when non-nil, receives accept-loop warnings. Nil falls
	// back to a default warnings-only stderr Reporter so library code
	// never panics.
	Reporter *diag.Reporter

	// IdleTimeout, when > 0, makes the server self-stop after no
	// requests have been seen for the given duration. Updated on every
	// dispatch. Zero (the default) disables auto-shutdown.
	IdleTimeout time.Duration
	// contains filtered or unexported fields
}

Server is a long-lived process that accepts daemon Requests on a Unix socket and dispatches each to a registered Handler.

func NewServer

func NewServer(socketPath string) *Server

NewServer returns a Server bound to socketPath. The socket file is created with mode 0600 when Start is called.

func (*Server) Register

func (s *Server) Register(verb string, h Handler)

Register attaches a Handler for verb. Registering the same verb twice replaces the prior handler.

func (*Server) SocketPath

func (s *Server) SocketPath() string

SocketPath returns the configured socket path.

func (*Server) Start

func (s *Server) Start(ctx context.Context) error

Start begins listening. It returns once the listener is ready (or an error if it could not bind). Connection accept and dispatch run in background goroutines until Stop is called or the listener errors.

func (*Server) Stop

func (s *Server) Stop()

Stop closes the listener and waits for in-flight connections to drain. Safe to call multiple times.

func (*Server) Wait

func (s *Server) Wait()

Wait blocks until Stop has completed and all in-flight handlers finish.

type StatusResult

type StatusResult struct {
	Ready       bool    `json:"ready"`
	Root        string  `json:"root"`
	Modules     int     `json:"modules"`
	Files       int     `json:"files"`
	WarmSeconds float64 `json:"warm_seconds"`
	// KritVersion is the daemon binary's compile-time version
	// string (e.g. "v0.42.0", "dev").
	KritVersion string `json:"krit_version,omitempty"`
	// BinaryHash is the SHA-256 of the daemon's running krit
	// binary. Clients with a different binary hash should restart
	// the daemon to avoid stale-protocol-or-rule-set drift.
	BinaryHash string `json:"binary_hash,omitempty"`
	// HasLibraryFacts reports whether the daemon's WorkspaceState
	// holds a cached *librarymodel.Facts. Useful for clients that
	// want to confirm cross-file warm state is populated before
	// running cross-file rules.
	HasLibraryFacts bool `json:"has_library_facts,omitempty"`
	// HasCodeIndex reports the same for *scanner.CodeIndex.
	HasCodeIndex bool `json:"has_code_index,omitempty"`
}

StatusResult reports daemon readiness and basic warm-up stats.

Jump to

Keyboard shortcuts

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