daemon

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package daemon implements the proxy daemon's management API.

Backwards Compatibility

The daemon is a long-lived process that may run a different binary version than the CLI or the moat-run process. This happens during development (rebuilding moat while runs are active) and during upgrades.

To keep old and new versions interoperable, the daemon API follows these rules:

  • Additive only: new fields in request/response structs are fine (encoding/json ignores unknown fields). Never remove or rename fields.
  • New endpoints are fine: old clients won't call them. New clients must handle 404 gracefully when talking to an older daemon.
  • Never change the semantics of existing fields.

When adding new API surface, consider: "will a CLI built today still work if the daemon is an older binary?" and vice versa.

Package daemon implements the proxy daemon for multi-run credential injection.

Index

Constants

This section is empty.

Variables

View Source
var BuildCommit string

BuildCommit is the git commit hash of the running binary. Set by the CLI at startup so the health endpoint can report the daemon's version. This allows diagnosing version skew between daemon and CLI.

View Source
var ErrRunNotFound = errors.New("run not found")

ErrRunNotFound is returned when a run is not registered with the daemon.

Functions

func RemoveLockFile

func RemoveLockFile(dir string)

RemoveLockFile removes the daemon lock file.

func StartTokenRefresh

func StartTokenRefresh(ctx context.Context, rc *RunContext, grants []string)

StartTokenRefresh begins a background goroutine that periodically refreshes credentials for the given run context.

func WriteLockFile

func WriteLockFile(dir string, info LockInfo) error

WriteLockFile writes the daemon lock file.

Types

type AWSConfig

type AWSConfig struct {
	RoleARN         string        `json:"role_arn"`
	Region          string        `json:"region"`
	SessionDuration time.Duration `json:"session_duration"`
	ExternalID      string        `json:"external_id,omitempty"`
}

AWSConfig holds AWS credential provider configuration.

type Client

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

Client communicates with the daemon over a Unix socket.

func EnsureRunning

func EnsureRunning(dir string, proxyPort int) (*Client, error)

EnsureRunning checks if the daemon is already running and returns a client. If not running, it starts the daemon via self-exec and waits for it to be ready.

An advisory file lock serializes the check-and-spawn sequence so concurrent callers don't each spawn a separate daemon process.

func NewClient

func NewClient(sockPath string) *Client

NewClient creates a daemon client connected to the given socket path.

func (*Client) Health

func (c *Client) Health(ctx context.Context) (*HealthResponse, error)

Health returns the daemon's health status.

func (*Client) ListRuns

func (c *Client) ListRuns(ctx context.Context) ([]RunInfo, error)

ListRuns returns all registered runs.

func (*Client) RegisterRoutes

func (c *Client) RegisterRoutes(ctx context.Context, agent string, services map[string]string) error

RegisterRoutes registers service routes for an agent.

func (*Client) RegisterRun

func (c *Client) RegisterRun(ctx context.Context, regReq RegisterRequest) (*RegisterResponse, error)

RegisterRun registers a new run with the daemon.

func (*Client) Shutdown

func (c *Client) Shutdown(ctx context.Context) error

Shutdown requests the daemon to shut down gracefully.

func (*Client) UnregisterRoutes

func (c *Client) UnregisterRoutes(ctx context.Context, agent string) error

UnregisterRoutes removes service routes for an agent.

func (*Client) UnregisterRun

func (c *Client) UnregisterRun(ctx context.Context, token string) error

UnregisterRun removes a run from the daemon.

func (*Client) UpdateRun

func (c *Client) UpdateRun(ctx context.Context, token, containerID string) error

UpdateRun updates a run's container ID (phase 2 of registration).

type CommandContainerChecker

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

CommandContainerChecker checks container liveness by shelling out to the container runtime CLI. It tries Docker first, then Apple containers.

func NewCommandContainerChecker

func NewCommandContainerChecker() *CommandContainerChecker

NewCommandContainerChecker creates a new container checker that uses CLI commands.

func (*CommandContainerChecker) IsContainerRunning

func (c *CommandContainerChecker) IsContainerRunning(ctx context.Context, id string) (bool, error)

IsContainerRunning checks if a container is still running. Returns (true, nil) when the container is confirmed alive, (false, nil) when confirmed dead, and (false, err) when the check itself failed (e.g. transient CLI error).

type ContainerChecker

type ContainerChecker interface {
	IsContainerRunning(ctx context.Context, id string) (alive bool, err error)
}

ContainerChecker checks if a container is still running. Returns (true, nil) when confirmed alive, (false, nil) when confirmed dead, and (false, err) when the check failed (transient error).

type CredentialEntry

type CredentialEntry struct {
	Name  string `json:"name"`  // Header name (e.g., "Authorization", "x-api-key")
	Value string `json:"value"` // Header value
	Grant string `json:"grant"` // Grant name for logging
}

CredentialEntry holds a credential header for proxy injection.

type CredentialSpec

type CredentialSpec struct {
	Host   string `json:"host"`
	Header string `json:"header"`
	Value  string `json:"value"`
	Grant  string `json:"grant,omitempty"`
}

CredentialSpec describes a credential to inject for a host.

type ExtraHeaderEntry

type ExtraHeaderEntry struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

ExtraHeaderEntry holds an additional header to inject.

type ExtraHeaderSpec

type ExtraHeaderSpec struct {
	Host       string `json:"host"`
	HeaderName string `json:"header_name"`
	Value      string `json:"value"`
}

ExtraHeaderSpec describes an additional header to inject.

type HealthResponse

type HealthResponse struct {
	PID       int    `json:"pid"`
	ProxyPort int    `json:"proxy_port"`
	RunCount  int    `json:"run_count"`
	StartedAt string `json:"started_at"`
	Commit    string `json:"commit,omitempty"` // Git commit hash of the daemon binary
}

HealthResponse is returned from GET /v1/health.

type IdleTimer

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

IdleTimer triggers a callback after a period of inactivity.

func NewIdleTimer

func NewIdleTimer(duration time.Duration, callback func()) *IdleTimer

NewIdleTimer creates an idle timer (does not start automatically).

func (*IdleTimer) Cancel

func (t *IdleTimer) Cancel()

Cancel stops the timer without firing.

func (*IdleTimer) Reset

func (t *IdleTimer) Reset()

Reset restarts the idle countdown.

type LivenessChecker

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

LivenessChecker periodically checks container liveness and cleans up dead runs.

func NewLivenessChecker

func NewLivenessChecker(registry *Registry, checker ContainerChecker) *LivenessChecker

NewLivenessChecker creates a new liveness checker with 30-second default interval.

func (*LivenessChecker) CheckOnce

func (lc *LivenessChecker) CheckOnce(ctx context.Context)

CheckOnce performs a single liveness check for all registered runs.

func (*LivenessChecker) Run

func (lc *LivenessChecker) Run(ctx context.Context)

Run starts the periodic liveness check loop. Blocks until ctx is canceled.

func (*LivenessChecker) SetOnCleanup

func (lc *LivenessChecker) SetOnCleanup(fn func(token, runID string))

SetOnCleanup sets a callback invoked when a run is cleaned up. The callback receives both the auth token and run ID.

func (*LivenessChecker) SetOnEmpty

func (lc *LivenessChecker) SetOnEmpty(fn func())

SetOnEmpty sets a callback invoked when the registry becomes empty after cleanup.

type LockInfo

type LockInfo struct {
	PID       int       `json:"pid"`
	ProxyPort int       `json:"proxy_port"`
	SockPath  string    `json:"sock_path"`
	StartedAt time.Time `json:"started_at"`
	Commit    string    `json:"commit,omitempty"` // Git commit hash of the daemon binary
}

LockInfo holds information about a running daemon.

func ReadLockFile

func ReadLockFile(dir string) (*LockInfo, error)

ReadLockFile reads the daemon lock file. Returns nil, nil if not found.

func (*LockInfo) IsAlive

func (l *LockInfo) IsAlive() bool

IsAlive checks if the daemon process is still running.

type RegisterRequest

type RegisterRequest struct {
	RunID                string                   `json:"run_id"`
	AuthToken            string                   `json:"auth_token,omitempty"` // Re-registration: use existing token
	Credentials          []CredentialSpec         `json:"credentials,omitempty"`
	ExtraHeaders         []ExtraHeaderSpec        `json:"extra_headers,omitempty"`
	RemoveHeaders        []RemoveHeaderSpec       `json:"remove_headers,omitempty"`
	TokenSubstitutions   []TokenSubstitutionSpec  `json:"token_substitutions,omitempty"`
	MCPServers           []config.MCPServerConfig `json:"mcp_servers,omitempty"`
	NetworkPolicy        string                   `json:"network_policy,omitempty"`
	NetworkAllow         []string                 `json:"network_allow,omitempty"`
	Grants               []string                 `json:"grants,omitempty"`
	AWSConfig            *AWSConfig               `json:"aws_config,omitempty"`
	ResponseTransformers []TransformerSpec        `json:"response_transformers,omitempty"`
}

RegisterRequest is sent to POST /v1/runs.

func (*RegisterRequest) ToRunContext

func (req *RegisterRequest) ToRunContext() *RunContext

ToRunContext converts a RegisterRequest into a RunContext.

type RegisterResponse

type RegisterResponse struct {
	AuthToken string `json:"auth_token"`
	ProxyPort int    `json:"proxy_port"`
}

RegisterResponse is returned from POST /v1/runs.

type Registry

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

Registry is a thread-safe mapping of auth tokens to RunContexts. It provides the central lookup mechanism for the daemon proxy to resolve incoming requests to their per-run configuration and credentials.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates an empty Registry.

func (*Registry) Count

func (r *Registry) Count() int

Count returns the number of registered runs.

func (*Registry) List

func (r *Registry) List() []*RunContext

List returns all registered RunContexts.

func (*Registry) Lookup

func (r *Registry) Lookup(token string) (*RunContext, bool)

Lookup finds a RunContext by auth token.

func (*Registry) Register

func (r *Registry) Register(rc *RunContext) string

Register adds a RunContext and returns a generated auth token. The token is a 32-byte cryptographically random hex string.

func (*Registry) RegisterWithToken

func (r *Registry) RegisterWithToken(rc *RunContext, token string)

RegisterWithToken adds a RunContext with a specific auth token. This is used for re-registration after a daemon restart so the container can keep using the same proxy auth token it was configured with.

func (*Registry) Unregister

func (r *Registry) Unregister(token string)

Unregister removes a RunContext by its auth token.

func (*Registry) UpdateContainerID

func (r *Registry) UpdateContainerID(token, containerID string) bool

UpdateContainerID sets the container ID for a registered run. Returns false if the token is not found.

type RemoveHeaderSpec

type RemoveHeaderSpec struct {
	Host       string `json:"host"`
	HeaderName string `json:"header_name"`
}

RemoveHeaderSpec describes a header to remove from requests.

type RouteRegistration

type RouteRegistration struct {
	Services map[string]string `json:"services"`
}

RouteRegistration is sent to POST /v1/routes/{agent}.

type RunContext

type RunContext struct {
	RunID       string `json:"run_id"`
	ContainerID string `json:"container_id,omitempty"`
	AuthToken   string `json:"auth_token"`

	Credentials          map[string]CredentialEntry                  `json:"credentials"`
	ExtraHeaders         map[string][]ExtraHeaderEntry               `json:"extra_headers"`
	RemoveHeaders        map[string][]string                         `json:"remove_headers"`
	TokenSubstitutions   map[string]TokenSubstitutionEntry           `json:"token_substitutions"`
	ResponseTransformers map[string][]credential.ResponseTransformer `json:"-"` // not serialized

	MCPServers    []config.MCPServerConfig `json:"mcp_servers,omitempty"`
	NetworkPolicy string                   `json:"network_policy,omitempty"`
	NetworkAllow  []string                 `json:"network_allow,omitempty"`

	AWSConfig        *AWSConfig        `json:"aws_config,omitempty"`
	TransformerSpecs []TransformerSpec `json:"transformer_specs,omitempty"`

	RegisteredAt time.Time `json:"registered_at"`
	// contains filtered or unexported fields
}

RunContext holds per-run proxy state. It implements credential.ProxyConfigurer so providers can configure it identically to how they configure proxy.Proxy.

func NewRunContext

func NewRunContext(runID string) *RunContext

NewRunContext creates a new RunContext for a run.

func (*RunContext) AddExtraHeader

func (rc *RunContext) AddExtraHeader(host, headerName, headerValue string)

AddExtraHeader implements credential.ProxyConfigurer.

func (*RunContext) AddResponseTransformer

func (rc *RunContext) AddResponseTransformer(host string, transformer credential.ResponseTransformer)

AddResponseTransformer implements credential.ProxyConfigurer.

func (*RunContext) CancelRefresh

func (rc *RunContext) CancelRefresh()

CancelRefresh cancels the token refresh goroutine, if any. Safe to call concurrently and multiple times.

func (*RunContext) GetContainerID

func (rc *RunContext) GetContainerID() string

GetContainerID returns the container ID safely.

func (*RunContext) GetCredential

func (rc *RunContext) GetCredential(host string) (CredentialEntry, bool)

GetCredential returns the credential for a host, checking host:port fallback.

func (*RunContext) GetExtraHeaders

func (rc *RunContext) GetExtraHeaders(host string) []ExtraHeaderEntry

GetExtraHeaders returns extra headers for a host, checking host:port fallback.

func (*RunContext) GetRemoveHeaders

func (rc *RunContext) GetRemoveHeaders(host string) []string

GetRemoveHeaders returns headers to remove for a host, checking host:port fallback.

func (*RunContext) GetResponseTransformers

func (rc *RunContext) GetResponseTransformers(host string) []credential.ResponseTransformer

GetResponseTransformers returns response transformers for a host, checking host:port fallback.

func (*RunContext) GetTokenSubstitution

func (rc *RunContext) GetTokenSubstitution(host string) (TokenSubstitutionEntry, bool)

GetTokenSubstitution returns the token substitution for a host, checking host:port fallback.

func (*RunContext) RemoveRequestHeader

func (rc *RunContext) RemoveRequestHeader(host, headerName string)

RemoveRequestHeader implements credential.ProxyConfigurer.

func (*RunContext) SetAWSHandler

func (rc *RunContext) SetAWSHandler(h http.Handler)

SetAWSHandler stores the AWS credential endpoint handler for this run.

func (*RunContext) SetCredential

func (rc *RunContext) SetCredential(host, value string)

SetCredential implements credential.ProxyConfigurer.

func (*RunContext) SetCredentialHeader

func (rc *RunContext) SetCredentialHeader(host, headerName, headerValue string)

SetCredentialHeader implements credential.ProxyConfigurer.

func (*RunContext) SetCredentialWithGrant

func (rc *RunContext) SetCredentialWithGrant(host, headerName, headerValue, grant string)

SetCredentialWithGrant implements credential.ProxyConfigurer.

func (*RunContext) SetRefreshCancel

func (rc *RunContext) SetRefreshCancel(cancel context.CancelFunc)

SetRefreshCancel stores the cancel function for the token refresh goroutine.

func (*RunContext) SetTokenSubstitution

func (rc *RunContext) SetTokenSubstitution(host, placeholder, realToken string)

SetTokenSubstitution implements credential.ProxyConfigurer.

func (*RunContext) ToProxyContextData

func (rc *RunContext) ToProxyContextData() *proxy.RunContextData

ToProxyContextData converts this RunContext into a proxy.RunContextData for use in per-request credential resolution.

type RunInfo

type RunInfo struct {
	RunID        string `json:"run_id"`
	ContainerID  string `json:"container_id,omitempty"`
	RegisteredAt string `json:"registered_at"`
}

RunInfo is an element of the list returned by GET /v1/runs.

type Server

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

Server is the daemon's HTTP API server over a Unix socket.

func NewServer

func NewServer(sockPath string, proxyPort int) *Server

NewServer creates a daemon API server that will listen on the given Unix socket path.

func (*Server) Registry

func (s *Server) Registry() *Registry

Registry returns the server's run registry.

func (*Server) SetOnEmpty

func (s *Server) SetOnEmpty(fn func())

SetOnEmpty sets a callback that is invoked when the last run is unregistered.

func (*Server) SetOnRegister

func (s *Server) SetOnRegister(fn func())

SetOnRegister sets a callback invoked when a new run is registered.

func (*Server) SetOnShutdown

func (s *Server) SetOnShutdown(fn func())

SetOnShutdown sets a callback that is invoked when shutdown is requested via the API. This should signal the main daemon loop to exit (e.g., by sending SIGTERM to self).

func (*Server) SetOnUnregister

func (s *Server) SetOnUnregister(fn func(runID string))

SetOnUnregister sets a callback that is invoked when a run is unregistered. The callback receives the run ID for per-run resource cleanup.

func (*Server) SetProxyPort

func (s *Server) SetProxyPort(port int)

SetProxyPort updates the proxy port reported in API responses. Call after the credential proxy starts to set the actual port.

func (*Server) SetRoutes

func (s *Server) SetRoutes(rt *routing.RouteTable)

SetRoutes sets the route table used for route registration handlers.

func (*Server) Start

func (s *Server) Start() error

Start begins listening on the Unix socket. Any stale socket file is removed first.

func (*Server) Stop

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

Stop gracefully shuts down the server and removes the socket file.

type TokenSubstitutionEntry

type TokenSubstitutionEntry struct {
	Placeholder string `json:"placeholder"`
	RealToken   string `json:"real_token"`
}

TokenSubstitutionEntry holds a placeholder-to-real-token mapping.

type TokenSubstitutionSpec

type TokenSubstitutionSpec struct {
	Host        string `json:"host"`
	Placeholder string `json:"placeholder"`
	RealToken   string `json:"real_token"`
}

TokenSubstitutionSpec describes a token substitution.

type TransformerSpec

type TransformerSpec struct {
	Host string `json:"host"`
	Kind string `json:"kind"` // "oauth-endpoint-workaround" or "response-scrub"
}

TransformerSpec describes a response transformer to apply for a host. Since transformers are Go functions (not serializable), this spec allows the daemon to reconstruct them from well-known kinds.

type UpdateRunRequest

type UpdateRunRequest struct {
	ContainerID string `json:"container_id"`
}

UpdateRunRequest is sent to PATCH /v1/runs/{token}.

Jump to

Keyboard shortcuts

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